package com.iflytek.cosmo.ocr.service.impl;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.AppendFileStorageClient;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.iflytek.cosmo.ocr.common.PageResult;
import com.iflytek.cosmo.ocr.common.config.ArgusProperties;
import com.iflytek.cosmo.ocr.common.config.ExampleFileByteConfig;
import com.iflytek.cosmo.ocr.common.config.ExportProperties;
import com.iflytek.cosmo.ocr.common.config.WorksProperties;
import com.iflytek.cosmo.ocr.common.constant.CommonConstant;
import com.iflytek.cosmo.ocr.common.constant.FileConstant;
import com.iflytek.cosmo.ocr.common.core.BeanHelper;
import com.iflytek.cosmo.ocr.common.core.TaskDispatchSystem;
import com.iflytek.cosmo.ocr.common.core.task.FormatTask;
import com.iflytek.cosmo.ocr.common.core.task.RecognizedTask;
import com.iflytek.cosmo.ocr.common.core.worker.CompressFileExportWorker;
import com.iflytek.cosmo.ocr.common.core.worker.NormalFileExportWorker;
import com.iflytek.cosmo.ocr.common.enums.ExportTaskStatusEnum;
import com.iflytek.cosmo.ocr.common.enums.FileStatusEnum;
import com.iflytek.cosmo.ocr.common.enums.ResultCodeEnum;
import com.iflytek.cosmo.ocr.common.exception.FileException;
import com.iflytek.cosmo.ocr.common.json.Jackson;
import com.iflytek.cosmo.ocr.common.util.ContUtil;
import com.iflytek.cosmo.ocr.common.util.FileUtil;
import com.iflytek.cosmo.ocr.common.util.ImageUtil;
import com.iflytek.cosmo.ocr.common.util.IpAddress;
import com.iflytek.cosmo.ocr.common.util.JSONUtil;
import com.iflytek.cosmo.ocr.common.util.PoliceDataExchangeUtils;
import com.iflytek.cosmo.ocr.common.util.UUIDUtil;
import com.iflytek.cosmo.ocr.dao.ExportTaskDao;
import com.iflytek.cosmo.ocr.dao.OcrAdvFuncDao;
import com.iflytek.cosmo.ocr.dao.OcrDirectoryDao;
import com.iflytek.cosmo.ocr.dao.OcrFileDao;
import com.iflytek.cosmo.ocr.dao.OcrResultDao;
import com.iflytek.cosmo.ocr.model.domain.ExportFileHtml;
import com.iflytek.cosmo.ocr.model.domain.ExportFileInfo;
import com.iflytek.cosmo.ocr.model.domain.FileInfo;
import com.iflytek.cosmo.ocr.model.domain.MessageOutput;
import com.iflytek.cosmo.ocr.model.domain.OutputMessage;
import com.iflytek.cosmo.ocr.model.domain.State;
import com.iflytek.cosmo.ocr.model.entity.ExportTaskEntity;
import com.iflytek.cosmo.ocr.model.entity.OcrAdvFuncEntity;
import com.iflytek.cosmo.ocr.model.entity.OcrDirectoryEntity;
import com.iflytek.cosmo.ocr.model.entity.OcrFileEntity;
import com.iflytek.cosmo.ocr.model.entity.OcrResultEntity;
import com.iflytek.cosmo.ocr.service.ArgusPostService;
import com.iflytek.cosmo.ocr.service.ExportTaskService;
import com.iflytek.cosmo.ocr.service.FileService;
import com.iflytek.cosmo.ocr.service.RecogniseService;
import com.iflytek.cosmo.ocr.service.RedisService;
import com.iflytek.cosmo.ocr.support.uap.UapClient;
import com.iflytek.icourt.common.constants.Constants;
import com.iflytek.icourt.common.util.FileExportUtil;
import com.iflytek.icourt.common.util.PdfComposeUtil;
import com.iflytek.icourt.file.export.FileExportStrategy;
import com.iflytek.icourt.file.export.impl.RtfDocxHtmlFileExportStrategy;
import com.iflytek.icourt.file.export.impl.TxtFileExportStrategy;
import com.iflytek.icourt.file.export.impl.XlsxCsvFileExportStrategy;
import com.iflytek.icourt.model.pdf.Char;
import com.iflytek.icourt.model.pdf.OcrResult;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.encryption.InvalidPasswordException;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.entity.Example.Criteria;

/**
 * @author liangxu5@iflytek.com
 * @version 1.0
 * @ClassName: FileServiceImpl.java
 * @Description: 该类的功能描述
 */

@Service
public class FileServiceImpl implements FileService {

    public static final String HIGHLIGHT_PATTERN = "<span class=\"fileSearch-result-list-item-span\">%s</span>";
    private static final  Integer FILE_DELETE = 0;
    /**
     * 日志记录器
     */
    private Logger logger = LoggerFactory.getLogger(FileServiceImpl.class);

    // 单文件/合并导出线程池
    private final ThreadPoolExecutor normalExportExecutor = new ThreadPoolExecutor(16, 16, 0, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(1000),
            new ThreadFactoryBuilder().setDaemon(false).setNameFormat("File-Exportor-%d").setUncaughtExceptionHandler(
                    ((t, e) -> logger.error("线程 " + t + " 异常结束", e))).build(),
            new ThreadPoolExecutor.CallerRunsPolicy());

    // 压缩导出线程池
    private final ThreadPoolExecutor compressExportExecutor = new ThreadPoolExecutor(16, 16, 0, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(500),
            new ThreadFactoryBuilder().setDaemon(false).setNameFormat("Compress-Exportor-%d").setUncaughtExceptionHandler(
                    ((t, e) -> logger.error("线程 " + t + " 异常结束", e))).build(),
            new ThreadPoolExecutor.CallerRunsPolicy());


    @Autowired
    ExportTaskService exportTaskService;
    /**
     * 文件导出任务
     */
    @Autowired
    ExportTaskDao exportTaskDao;

    /**
     * 导出设置
     */
    @Autowired
    private ExportProperties exportProperties;

    /**
     * ocrFileDao
     */
    @Autowired
    private OcrFileDao ocrFileDao;

   /* @Autowired
    private FdfsProperties fdfsProperties;*/

    @Resource(name = "pdfCreateExecutor")
    private ThreadPoolTaskExecutor PDF_CREATE_THREAD_POOL;

    @Resource(name = "deleteFileExecutor")
    private ThreadPoolTaskExecutor DELETE_FILE_THREAD_POOL;

    @Value("${cosmo.base-url}")
    private String cosmoUrl;

    @Autowired
    private RestTemplate restTemplate;

    /**
     * OcrDirectoryDao
     */
    @Autowired
    private OcrDirectoryDao ocrDirectoryDao;

    @Autowired
    private OcrResultDao ocrResultDao;

    /**
     * storageClient
     */
    @Autowired
    private AppendFileStorageClient storageClient;

    /**
     * UapClient
     */
    @Autowired
    private UapClient uapClient;


    /**
     * ArgusPostService
     */
    @Autowired
    private ArgusPostService argusPostService;

    /**
     * ArgusProperties  (zhouwei更改)
     */
    @Autowired
    private ArgusProperties argusProperties;

    @Value("${fdfs.url}")
    private String fdfsUrl;

    @Autowired
    private TxtFileExportStrategy txtFileExportStrategy;

    @Autowired
    private XlsxCsvFileExportStrategy xlsxCsvFileExportStrategy;

    @Autowired
    private RtfDocxHtmlFileExportStrategy rtfDocxHtmlFileExportStrategy;
    
    @Autowired
    private OcrAdvFuncDao ocrAdvFuncDao;

    @Autowired
    private ExampleFileByteConfig exampleFileByteConfig;

    @Autowired
	private WorksProperties worksProperties;

    @Autowired
	private RecogniseService recogniseService;
    
    @Value("${delete.type.logic:true}")
    private Boolean deleteType;

    @Autowired
    private RedisService redisService;

    @Override
    public String uploadFile(MultipartFile files) throws Exception {
        String fileName = files.getOriginalFilename();
        String fileType = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
        /*String storePath = storageClient.uploadFile(files.getInputStream(), (int) files.getSize(), fileType);*/
        byte[] in_b = FileUtil.inputStream2ByteArray(files.getInputStream());
        StorePath path = storageClient.uploadFile(null, files.getInputStream(), in_b.length, fileType);
        String storePath = path.getFullPath();
        if (StringUtils.isBlank(storePath)) {
            logger.error(fileName + "上传失败");
            throw new FileException("文件上传失败");
        }
        return storePath;
    }


    @Override
    public String uploadFile(String fileName, String fileType, byte[] bytes) throws Exception {
        /* String storePath = storageClient.uploadFile(new ByteArrayInputStream(bytes), bytes.length, fileType);*/
        StorePath path = storageClient.uploadFile("", new ByteArrayInputStream(bytes), bytes.length, fileType);
        String storePath = path.getFullPath();
        if (StringUtils.isBlank(storePath)) {
            logger.error(fileName + "上传失败");
            throw new FileException("文件上传失败");
        }
        return storePath;
    }

    @Override
    public String saveFileInfo(FileInfo fileInfo) {
        if (null == fileInfo) {
            logger.info("保存失败，缺少对应的文件信息");
        }
        OcrFileEntity ocrFileEntity = new OcrFileEntity();
        BeanUtils.copyProperties(fileInfo, ocrFileEntity);
        // 查询条件
        Example example = new Example(OcrFileEntity.class);
        Criteria criteria = example.createCriteria();
        // id条件
        String id = ocrFileEntity.getId();
        List<OcrFileEntity> ocrFileList = null;
        if (StringUtils.isNotBlank(id)) {
            criteria.andEqualTo("id", id);
            ocrFileList = ocrFileDao.selectByExample(example);
        }
        Date current = new Date();
        if (id != null && StringUtils.isNotBlank(id) && ocrFileList != null && ocrFileList.size() > 0) {
            // 更新
            ocrFileEntity.setUpdateTime(current);
            ocrFileDao.updateByExampleSelective(ocrFileEntity, example);
        } else {
            // 新增
            ocrFileEntity.setId(UUIDUtil.getUUID());
            ocrFileEntity.setCreateTime(current);
            ocrFileEntity.setUpdateTime(current);
            if (ocrFileEntity.getStatus() == null) {
                ocrFileEntity.setStatus(FileStatusEnum.INIT.getCode());
            }
            ocrFileEntity.setIsDelete(FILE_DELETE);
            ocrFileDao.insert(ocrFileEntity);
        }
        return ocrFileEntity.getId();
    }

    @Override
    public byte[] getFileBytes(String filePath) {
        byte[] FileBytes = null;
        try {
            FileBytes = PoliceDataExchangeUtils.downloadFile(fdfsUrl + "/" + filePath, "", "");
        } catch (Exception e) {
            logger.info(">>>获取文件字节流失败！");
            return null;
        }
        return FileBytes;
    }

    @Override
    public FileInfo selectFileInfo(String fileId) {
        OcrFileEntity ocrFileEntity = new OcrFileEntity();
        ocrFileEntity.setId(fileId);
        OcrFileEntity file = ocrFileDao.selectOne(ocrFileEntity);
        FileInfo fileInfoInput = new FileInfo();
        if (null != file) {
            BeanUtils.copyProperties(file, fileInfoInput);
        }
        return fileInfoInput;
    }

    @Override
    public void downloadFile(HttpServletResponse response, String fileId, String ip) throws IOException {
        OcrFileEntity ocrFileEntity = new OcrFileEntity();
        ocrFileEntity.setId(fileId);
        OcrFileEntity fileInfo = ocrFileDao.selectOne(ocrFileEntity);
        if (null != fileInfo) {
            String filePath = fileInfo.getFilePath();
            // String fileName = URLEncoder.encode(fileInfo.getFileName() + "." + fileInfo.getFileType(), "UTF-8");
            String fileName = fileInfo.getFileName() + "." + fileInfo.getFileType();
            Pattern pattern = Pattern
                    .compile("[\u4E00-\u9FA5|\\！|\\,|\\。|\\（|\\）|\\《|\\》|\\“|\\”|\\？|\\：|\\；|\\【|\\】]+");
            Matcher matcher = pattern.matcher(fileName);
            while (matcher.find()) {
                String mStr = matcher.group();
                try {
                    fileName = fileName.replaceFirst(mStr, URLEncoder.encode(mStr, "UTF-8"));
                } catch (Exception e) {

                }
            }

            //给用户下载文件增加埋点(zhouwei)
            if (argusProperties.getBuryingEnable()) {
                String userId = fileInfo.getUserId();
                String fileType = fileInfo.getFileType();
                switch (fileType) {
                    case "png":
                        argusPostService.postAgent(ip, argusProperties.getOcrDownloadPngNum(), 1, "userId=" + userId, null);
                        break;
                    case "pdf":
                        argusPostService.postAgent(ip, argusProperties.getOcrDownloadPdfNum(), 1, "userId=" + userId, null);
                        break;
                    case "jpg":
                        argusPostService.postAgent(ip, argusProperties.getOcrDownloadJpgNum(), 1, "userId=" + userId, null);
                        break;
                    case "jpeg":
                        argusPostService.postAgent(ip, argusProperties.getOcrDownloadJpegNum(), 1, "userId=" + userId, null);
                        break;
                    case "bmp":
                        argusPostService.postAgent(ip, argusProperties.getOcrDownloadBmpNum(), 1, "userId=" + userId, null);
                        break;
                    case "tif":
                        argusPostService.postAgent(ip, argusProperties.getOcrDownloadTifNum(), 1, "userId=" + userId, null);
                        break;
                    case "tiff":
                        argusPostService.postAgent(ip, argusProperties.getOcrDownloadTiffNum(), 1, "userId=" + userId, null);
                        break;
                    default:
                        break;
                }
            }

            byte[] fileBytes = getFileBytes(filePath);
            response.reset();
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
            response.addHeader("Content-Length", "" + fileBytes.length);
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.getOutputStream().write(fileBytes);
            response.getOutputStream().flush();
            response.getOutputStream().close();
        }
    }

    @Override
    public MessageOutput<Object> renameFile(FileInfo fileInfo) {
        if (StringUtils.isBlank(fileInfo.getId()) || StringUtils.isBlank(fileInfo.getFileName())) {
            return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE, FileConstant.FILE_RENAME_FAILURES));
        }
        ocrFileDao.renameFile(fileInfo);

        // 删除关联的导出任务记录
        exportTaskService.cleanupByFileId(fileInfo.getId());

        return new MessageOutput<Object>(new State(CommonConstant.SUCCESS_CODE, null));
    }

    @Override
    public MessageOutput<Object> deleteFile(String fileId) {

        if (StringUtils.isBlank(fileId)) {
            return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE, FileConstant.FILE_DELETE_FAILURES));
        }
        OcrFileEntity ocrFileEntity = new OcrFileEntity();
        ocrFileEntity.setId(fileId);
        OcrFileEntity fileEntity = ocrFileDao.selectOne(ocrFileEntity);//源文件
        if (null == fileEntity) {
            return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE, FileConstant.FILE_NO_EXISTS));
        }
        if(this.deleteType) {
       	   //删除父文件
           Example example = new Example(OcrFileEntity.class);
           Example.Criteria criteria = example.createCriteria();
           criteria.andEqualTo("id",fileId);
           OcrFileEntity ocrFile = new OcrFileEntity();
           ocrFile.setUpdateTime(new Date());
           ocrFile.setIsDelete(1);
           ocrFileDao.updateByExampleSelective(ocrFile,example);
           // 删除子文件
           Example exampleSon = new Example(OcrFileEntity.class);
           Example.Criteria criteriaSon = exampleSon.createCriteria();
           criteriaSon.andEqualTo("parentId",fileId);
           OcrFileEntity ocrFileSon = new OcrFileEntity();
           ocrFileSon.setUpdateTime(new Date());
           ocrFileSon.setIsDelete(1);
           ocrFileDao.updateByExampleSelective(ocrFileSon,exampleSon);

           // 删除子文件识别结果
           Example exampleResult = new Example(OcrResultEntity.class);
           Example.Criteria criteriaResult = exampleResult.createCriteria();
           criteriaResult.andEqualTo("groupId",fileId);
           OcrResultEntity resultEntity = new OcrResultEntity();
           resultEntity.setUpdateTime(new Date());
           resultEntity.setIsDelete(1);
           ocrResultDao.updateByExampleSelective(resultEntity,exampleResult);
           
           //查询子文件
           FileInfo fileInfo = new FileInfo();
           fileInfo.setParentId(fileId);
           List<String> fileIds =new ArrayList<>();
           List<OcrFileEntity> subFileList = getSubFile(fileInfo);
           if(CollectionUtils.isNotEmpty(subFileList)) {
        	   fileIds.addAll(subFileList.stream().map(OcrFileEntity::getId).collect(Collectors.toList()));
           }
           fileIds.add(fileId);
           //获取高级功能表中对应文件+子文件
           List<OcrAdvFuncEntity> ocrAdvFuncEntities = ocrAdvFuncDao.getAdvFuncRecords(fileIds);
           if (CollectionUtils.isEmpty(ocrAdvFuncEntities)){
               return new MessageOutput<Object>(new State(CommonConstant.SUCCESS_CODE, null));
           }
           List<String> advFuncIds = ocrAdvFuncEntities.stream().map(OcrAdvFuncEntity::getId).collect(Collectors.toList());
           
           //删除高级功能图片识别结果
           Example exampleAdvResult = new Example(OcrResultEntity.class);
           Example.Criteria criteriaAdvResult = exampleAdvResult.createCriteria();
           criteriaAdvResult.andIn("fileId", advFuncIds);
           OcrResultEntity resultAdvEntity = new OcrResultEntity();
           resultAdvEntity.setUpdateTime(new Date());
           resultAdvEntity.setIsDelete(1);
           ocrResultDao.updateByExampleSelective(resultAdvEntity,exampleAdvResult);
           //删除高级功能图片
           Example exampleAdvFunFile = new Example(OcrFileEntity.class);
           Criteria criteriaAdvFunFile = exampleAdvFunFile.createCriteria();
           criteriaAdvFunFile.andIn("id", advFuncIds);
           OcrFileEntity ocrAdvFunFile = new OcrFileEntity();
           ocrAdvFunFile.setUpdateTime(new Date());
           ocrAdvFunFile.setIsDelete(1);
           ocrFileDao.updateByExampleSelective(ocrFileSon,exampleAdvFunFile);
           //删除高级功能
           Example exampleAdvFun = new Example(OcrAdvFuncEntity.class);
           Example.Criteria criteriaAdvFun = exampleAdvFun.createCriteria();
           criteriaAdvFun.andIn("id", advFuncIds);
           OcrAdvFuncEntity ocrAdvFuncEntity = new OcrAdvFuncEntity();
           ocrAdvFuncEntity.setUpdateTime(new Date());
           ocrAdvFuncEntity.setIsDelete(1);
           ocrAdvFuncDao.updateByExampleSelective(ocrAdvFuncEntity,exampleAdvFun);

           return new MessageOutput<Object>(new State(CommonConstant.SUCCESS_CODE, null));
        }
        List<OcrFileEntity> fileEntityList = new ArrayList<>();
        fileEntityList.add(fileEntity);
        if ("pdf".equalsIgnoreCase(fileEntity.getFileType())){//pdf的删除方式
            FileInfo fileInfo = new FileInfo();
            fileInfo.setParentId(fileId);
            List<OcrFileEntity> fileList = getSubFile(fileInfo);//查询子文件
            if (CollectionUtils.isEmpty(fileList)){//pdf识别错误，无子文件，删除源文件与文件信息
                fastdfsDeleteFile(fileEntityList);
                ocrFileDao.deleteFile(fileId);
                return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE, FileConstant.FILE_NO_EXISTS));
            }
            //删除保存在fastdfs上的源文件
            fastdfsDeleteFile(fileEntityList);
            //通过id删除t_ocr_file中的源文件记录
            ocrFileDao.deleteFile(fileId);
            //删除pdf子文件的图片
            fastdfsDeleteFile(fileList);
            //删除子文件在t_ocr_file中的记录（一次性批量删除）
            ocrFileDao.deleteFileByParent(fileId);
            //删除子文件的识别结果（一次性批量删除）
            ocrResultDao.deleteFileByGroupId(fileId);
            //循环删除高级功能中的存在的记录
            //1.获取子文件id的list集合
            List<String> subFileId = new ArrayList<>();
            for (OcrFileEntity subFile : fileList){
                subFileId.add(subFile.getId());
            }
            fileList = null;
            //2.获取高级功能表中对应子文件的记录
            List<OcrAdvFuncEntity> ocrAdvFuncEntities = ocrAdvFuncDao.getAdvFuncRecords(subFileId);
            //高级功能对应记录为空直接返回
            if (CollectionUtils.isEmpty(ocrAdvFuncEntities)){
                return new MessageOutput<Object>(new State(CommonConstant.SUCCESS_CODE, null));
            }
            //删除文件高级功能id集合
            List<String> advFuncId = new ArrayList<>();
            List<OcrFileEntity> advFuncPath = new ArrayList<>();//将高级功能图片路径封装到OcrFileEntity中，以便调用多线程删除
            for (OcrAdvFuncEntity ocrAdvFuncEntity : ocrAdvFuncEntities){
                advFuncId.add(ocrAdvFuncEntity.getId());
                OcrFileEntity ocrFileEntity1 = new OcrFileEntity();
                ocrFileEntity1.setFilePath(ocrAdvFuncEntity.getAdvRecoPath());
                ocrFileEntity1.setFormatPath(ocrAdvFuncEntity.getAdvCompPath());
                advFuncPath.add(ocrFileEntity1);
            }
            //删除高级功能表中数据
            ocrAdvFuncDao.deleteAdvFuncRecords(advFuncId);
            //删除fastdfs中的图片
            fastdfsDeleteFile(advFuncPath);
            //删除t_ocr_file中高级功能记录
            ocrFileDao.deleteAdvFuncFile(advFuncId);
            //删除识别记录
            ocrResultDao.deleteAdvFuncResult(advFuncId);
            return new MessageOutput<Object>(new State(CommonConstant.SUCCESS_CODE, null));
        }else {
            //非pdf文件的删除方式
            //1.删除fastdfs上的源文件
            fastdfsDeleteFile(fileEntityList);
            //2.删除file中的记录
            ocrFileDao.deleteByPrimaryKey(fileId);
            //删除原始识别结果
            ocrResultDao.deleteFileByGroupId(fileId);
            //3.查询高级功能表
            OcrAdvFuncEntity ocrAdvFuncEntity = new OcrAdvFuncEntity();
            ocrAdvFuncEntity.setFileId(fileId);
            List<OcrAdvFuncEntity> ocrAdvFuncEntities = ocrAdvFuncDao.select(ocrAdvFuncEntity);
            if (CollectionUtils.isEmpty(ocrAdvFuncEntities)){
                return new MessageOutput<Object>(new State(CommonConstant.SUCCESS_CODE, null));
            }
            //遍历list得到高级功能表记录的id
            List<String> advFuncId = new ArrayList<>();
            List<OcrFileEntity> advFuncPath = new ArrayList<>();//将高级功能图片路径封装到OcrFileEntity中，以便调用多线程删除
            for (OcrAdvFuncEntity ocrAdvFuncEntity1 : ocrAdvFuncEntities){
                advFuncId.add(ocrAdvFuncEntity1.getId());
                OcrFileEntity ocrFileEntity1 = new OcrFileEntity();
                ocrFileEntity1.setFilePath(ocrAdvFuncEntity1.getAdvRecoPath());
                ocrFileEntity1.setFormatPath(ocrAdvFuncEntity1.getAdvCompPath());
                advFuncPath.add(ocrFileEntity1);
            }
            //删除高级功能表中数据
            ocrAdvFuncDao.deleteAdvFuncRecords(advFuncId);
            //删除fastdfs中的图片
            fastdfsDeleteFile(advFuncPath);
            //删除t_ocr_file中高级功能记录
            ocrFileDao.deleteAdvFuncFile(advFuncId);
            //删除识别记录
            ocrResultDao.deleteAdvFuncResult(advFuncId);
            return new MessageOutput<Object>(new State(CommonConstant.SUCCESS_CODE, null));
        }




        /*// 删除fdfs上文件
        try {
            List<OcrFileEntity> fileEntityList = new ArrayList<>();
            fileEntityList.add(fileEntity);
            fastdfsDeleteFile(fileEntityList);
            // 子文件若存在，则删除子文件
            FileInfo fileInfo = new FileInfo();
            fileInfo.setParentId(fileId);
            List<OcrFileEntity> fileList = getSubFile(fileInfo);
            if (fileList != null && fileList.size() > 0) {
                // 删除数据库子文件记录
                ocrFileDao.deleteFileByParent(fileId);
                ocrResultDao.deleteFileByGroupId(fileId);
                //换用多线程删除
                fastdfsDeleteFile(fileList);

            }
        } catch (Exception e) {
            logger.info(">>>>>fdfs删除文件失败");
            return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE, FileConstant.FILE_DELETE_FAILURES));
        }
        // 删除数据库里文件信息
        ocrFileDao.deleteFile(fileId);
        OcrResultEntity or = new OcrResultEntity();
        or.setGroupId(fileId);
        ocrResultDao.delete(or);
        return new MessageOutput<Object>(new State(CommonConstant.SUCCESS_CODE, null));*/
    }

    private void fastdfsDeleteFile(List<OcrFileEntity> fileList){
        final CountDownLatch countDownLatch = new CountDownLatch(fileList.size());
        fileList.forEach(fi->{
            DELETE_FILE_THREAD_POOL.execute(()->{
                try {
                    logger.info(">>>>>>>>>>>开始删除文件");
                    if (fi.getFilePath() != null) {
                        /*storageClient.deleteFile(fi.getFilePath());*/
                        String path = fi.getFilePath();
                        String groupName = path.substring(0, path.indexOf("/"));
                        String attpath = path.substring(path.indexOf("/") + 1);
                        storageClient.deleteFile(groupName, attpath);
                    }
                    if (fi.getFormatPath() != null) {
                        /*storageClient.deleteFile(fi.getFormatPath());*/
                        String path = fi.getFormatPath();
                        String groupName = path.substring(0, path.indexOf("/"));
                        String attpath = path.substring(path.indexOf("/") + 1);
                        storageClient.deleteFile(groupName, attpath);
                    }
                }catch (Exception ex){
                    logger.info(">>>>>>>>>>>删除子文件失败");
                }
            });
        });
    }

    @Override
    public List<OcrFileEntity> getAllFile(FileInfo fileInfo) {
        String dirId = fileInfo.getDirId();
        String userId = fileInfo.getUserId();
        if(StringUtils.isNotEmpty(dirId) && StringUtils.isNotEmpty(userId)){
            String[] dirIdArr = dirId.split(",");
            ArrayList<OcrFileEntity> fileList = new ArrayList<>();//存储需要返回的文件
            ArrayList<OcrFileEntity> selfDefine = new ArrayList<>();//存储自定义文件
            List<OcrFileEntity> fileListDefault = null;//存储默认文件
            List<OcrFileEntity> fileListSample = null;//存储示例文件
            for (int i = 0; i < dirIdArr.length; i++){
                OcrDirectoryEntity ocrDirectoryEntity = ocrDirectoryDao.selectByPrimaryKey(dirIdArr[i]);//查询文件夹信息，以判断文件夹的类型
                FileInfo fileInfo1 = new FileInfo();
                fileInfo1.setDirId(dirIdArr[i]);
                fileInfo1.setUserId(userId);
                fileInfo1.setIsDelete(FILE_DELETE);
                switch (ocrDirectoryEntity.getDirName()){//根据查询的文件夹信息，获取文件夹名称
                    case CommonConstant.DIR_TYPE_DEFAULT_NAME:
                        fileListDefault = ocrFileDao.selectAllFile(fileInfo1);
                        if (CollectionUtils.isNotEmpty(fileListDefault)){
                            ContUtil.fileNameSort(fileListDefault);//对文件进行名称排序
                        }
                        break;
                    case CommonConstant.DIR_TYPE_EXAMPLE_NAME:
                        fileListSample = ocrFileDao.selectAllFile(fileInfo1);
                        if (CollectionUtils.isNotEmpty(fileListSample)){
                            ContUtil.fileNameSort(fileListSample);//对文件进行名称排序
                        }
                        break;
                    default:
                        List<OcrFileEntity> fileListDefine = ocrFileDao.selectAllFile(fileInfo1);
                        if (CollectionUtils.isNotEmpty(fileListDefine)){
                            ContUtil.fileNameSort(fileListDefine);//对文件进行名称排序
                            selfDefine.addAll(fileListDefine);
                            fileListDefine = null;
                        }
                        break;
                }
            }
            if(CollectionUtils.isNotEmpty(fileListDefault)) {
            	fileList.addAll(fileListDefault);
            	fileListDefault = null;
            }
            if(CollectionUtils.isNotEmpty(fileListSample)) {
            	//返回示例文件类型
            	fileListSample.parallelStream().forEach(v->{
            		String fileName=v.getFileName();
            		Set<Integer> exampleType=new HashSet<>();
            		Map<String, Integer> map=exampleFileByteConfig.getHashMap();
            		for(Map.Entry<String, Integer> entry : map.entrySet()){
            		    String mapKey = entry.getKey();
            		    Integer mapValue = entry.getValue();
            		    if(fileName.indexOf(mapKey) >-1) {
            		    	exampleType.add(mapValue);
            		    }
            		}
            		v.setExampleType(exampleType);

            	});
            	fileList.addAll(fileListSample);
                fileListSample = null;
            }
            if(CollectionUtils.isNotEmpty(selfDefine)) {
            	fileList.addAll(selfDefine);
                selfDefine = null;
            }
            return fileList;
        }
        return null;
    }

    @Override
    public List<OcrFileEntity> getSubFile(FileInfo fileInfo) {
        List<OcrFileEntity> fileList = new ArrayList<>();
        if (null != fileInfo && StringUtils.isNotBlank(fileInfo.getParentId())) {
            List<OcrFileEntity> resultList = ocrFileDao.selectSubFile(fileInfo.getParentId());
            if (resultList != null && resultList.size() > 0) {
                fileList = resultList;
            }
        }
        return fileList;
    }

    @Override
    public OutputMessage<Object> createDirectory(String dirName, String userId) {
        if (StringUtils.isNotEmpty(dirName) && StringUtils.isNotEmpty(userId)){
            OcrDirectoryEntity ode = new OcrDirectoryEntity();//文件夹封装参数实体
            Date current = new Date();
            String uuid = UUIDUtil.getUUID();
            ode.setId(uuid);
            ode.setDirName(dirName);
            ode.setUserId(userId);
            ode.setDirType(CommonConstant.DIR_TYPE_COMMON);//自定义文件夹
            ode.setCreateTime(current);
            int i = ocrDirectoryDao.selectDirName(ode);//查询某个用户是否有相同文件夹存在（自己写的查询语句），也可用mapper自带的查询
            if(i > 0){
                return new OutputMessage<>(1,"","文件夹已存在！");
            }
            int id = ocrDirectoryDao.createDirectory(ode);//数据库中插入文件夹信息
            HashMap<String, String> hm = new HashMap<>();
            hm.put("dirId" , uuid);
            if (id >= 1){
                return new OutputMessage<Object>(0, hm, "新建文件夹成功!");
            }else{
                return new OutputMessage<Object>(1, "", "新建文件夹失败!");
            }
        }
        return new OutputMessage<Object>(1, "", "请输入文件夹名称!");
    }

    @Override
    public OutputMessage<Object> modifyDirectory(String dirId, String dirName, String userId) {
        if (StringUtils.isNotEmpty(dirId) && StringUtils.isNotEmpty(dirName) && StringUtils.isNotEmpty(userId)){
            Date updateTime = new Date();
            OcrDirectoryEntity ocrDirectoryEntity = new OcrDirectoryEntity();
            ocrDirectoryEntity.setId(dirId);
            ocrDirectoryEntity.setDirName(dirName);
            ocrDirectoryEntity.setUserId(userId);
            ocrDirectoryEntity.setUpdateTime(updateTime);
            int num = ocrDirectoryDao.selectModify(ocrDirectoryEntity);//查询修改的文件夹名称是否在数据库中存在
            if (num > 0){
                return new OutputMessage<>(1,"","文件夹已存在！");
            }
            OcrDirectoryEntity ocrDirectoryEntity1 = ocrDirectoryDao.selectByPrimaryKey(dirId);//查询文件夹信息，以判断是否可修改
            if (ocrDirectoryEntity1.getDirType() == CommonConstant.DIR_TYPE_COMMON){
                if (ocrDirectoryEntity1.getDirName().equalsIgnoreCase(dirName)){//文件夹名称不变的话，不给任何提示语
                    return new OutputMessage<>(0, "", "文件夹名称未修改！");
                }
                //修改文件夹名称
                int i = ocrDirectoryDao.updateByPrimaryKeySelective(ocrDirectoryEntity);
                if (i >= 1){
                    // 查询t_ocr_file下的所有文件
                    Example example = new Example(OcrFileEntity.class);
                    example.createCriteria().andEqualTo("dirId", dirId);
                    List<OcrFileEntity> ocrFileEntities = ocrFileDao.selectByExample(example);
                    if (CollectionUtils.isEmpty(ocrFileEntities)){
                        return new OutputMessage<>(0,"" ,"文件夹名称修改成功！");
                    }
                    List<String> fileIds = ocrFileEntities.stream().map(t -> t.getId()).collect(Collectors.toList());
                    // 查询t_export_task中的fileids
                    Example example1 = new Example(ExportTaskEntity.class);
                    example1.createCriteria().andLike("fileIds", "%,%");
                    List<ExportTaskEntity> exportTaskEntities = exportTaskDao.selectByExample(example1);
                    if (CollectionUtils.isEmpty(exportTaskEntities)){
                        return new OutputMessage<>(0,"" ,"文件夹名称修改成功！");
                    }
                    List<String> exportFileIds = exportTaskEntities.stream().map(t -> t.getFileIds()).collect(Collectors.toList());
                    // 存储每一个导出的id
                    List<String> changFileNames = new ArrayList<>();
                    for (String exportFileId : exportFileIds){
                        String[] fileId = exportFileId.split(",");
                        List<String> everyFileIds = Arrays.asList(fileId);
                        for (String id : everyFileIds){
                            if(fileIds.contains(id)){
                                changFileNames.add(exportFileId);
                                break;
                            }else {
                                break;
                            }
                        }
                    }
                    //批量改名称
                    if (CollectionUtils.isNotEmpty(changFileNames)){
                        exportTaskDao.batchUpdateFileName(changFileNames, dirName);
                    }
                    return new OutputMessage<>(0,"" ,"文件夹名称修改成功！");
                }else{
                    return new OutputMessage<>(1,"", "文件夹名称修改失败！");
                }
            }else {
                return new OutputMessage<>(1,"","文件夹名称不可修改！");
            }
        }
        return new OutputMessage<>(1,"", "输入参数错误！");
    }

    @Override
    public OutputMessage<Object> deleteDirectory(String dirId) {
        if (!StringUtils.isNotEmpty(dirId)) {
            return new OutputMessage<>(1, "", "输入参数为空!");
        }
        boolean flag = false;//标志文件是否删除成功
        String[] dirIdArr = dirId.split(",");//多文件夹拆成文件夹id数组
        for (int i = 0; i < dirIdArr.length; i++) {//遍历每个文件文件夹,查看是否有文件
            //List<OcrFileEntity> fileEntityList = mapper
            //查询t_ocr_file中文件,（pdf文件情况下，包括子文件）
           /* List<OcrFileEntity> fileArrList = ocrFileDao.selectByExample(new Example.Builder(OcrFileEntity.class)
            .where(WeekendSqls.<OcrFileEntity>custom().andEqualTo(OcrFileEntity::getDirId,dirIdArr[i])
            .andIsNull(OcrFileEntity::getParentId)));*/
            Example example = new Example(OcrFileEntity.class);
            Criteria criteria = example.createCriteria();
            criteria.andEqualTo("dirId", dirIdArr[i]);
            criteria.andEqualTo("isDelete", FILE_DELETE);
            criteria.andIsNull("parentId");
            List<OcrFileEntity> fileArrList = ocrFileDao.selectByExample(example);//通过dirId查询（排除pdf分解的子文件）所有文件
            if (CollectionUtils.isNotEmpty(fileArrList)){
                fileArrList.forEach(deleteEntity-> deleteFile(deleteEntity.getId()));
                flag = true;
            }
        }
        if (flag) {
            return new OutputMessage<>(0, "", "删除文件成功!");
        } else {
            for (int k = 0; k < dirIdArr.length; k++) {
                OcrDirectoryEntity ocrDirectoryEntity1 = ocrDirectoryDao.selectByPrimaryKey(dirIdArr[k]);//查询空文件夹信息，判断是否能删除
                if (ocrDirectoryEntity1.getDirType() == CommonConstant.DIR_TYPE_COMMON) {//自定义空文件夹能删除
                	//逻辑删除文件夹
                	 if(deleteType)
                     {
                         Example ocrDirectoryEntityexample = new Example(OcrDirectoryEntity.class);
                         Example.Criteria ocrDirectoryEntityCriteria = ocrDirectoryEntityexample.createCriteria();
                         ocrDirectoryEntityCriteria.andEqualTo("id",dirIdArr[k]);
                         OcrDirectoryEntity ocrDirectoryEntity = new OcrDirectoryEntity();
                         ocrDirectoryEntity.setUpdateTime(new Date());
                         ocrDirectoryEntity.setIsDelete(1);
                         ocrDirectoryDao.updateByExampleSelective(ocrDirectoryEntity,ocrDirectoryEntityexample);
                     }else {
                    	 ocrDirectoryDao.deleteByPrimaryKey(ocrDirectoryEntity1);//查询文件为空，则删除删除文件夹
                     }
                }
            }
            return new OutputMessage<>(0, "", "删除文件夹成功!");
        }
    }

    @Override
    public List<OcrDirectoryEntity> getAllDirectory() {
        return ocrDirectoryDao.selectAll();
    }

    @Override
    public void exportFile(HttpServletResponse response, String fileId) throws IOException {
        // 重置结果集
        List<String> ocrResultList = new ArrayList<>();
        OcrFileEntity ofileEntity = new OcrFileEntity();
        ofileEntity.setId(fileId);
        OcrFileEntity ocrFileResult = ocrFileDao.selectOne(ofileEntity);
        if (null == ocrFileResult) {
            return;
        }
        String fileName = ocrFileResult.getFileName();
        String fileType = ocrFileResult.getFileType().toLowerCase();
        // pdf文件存在多页，其他文件是单页
        if ("pdf".equalsIgnoreCase(fileType)) {
            List<OcrFileEntity> files = ocrFileDao.selectSubFile(fileId);
            if (null == files || files.size() < 1) {
                return;
            }
            for (OcrFileEntity file : files) {
                OcrResultEntity ocrResultEntity = new OcrResultEntity();
                ocrResultEntity.setFileId(file.getId());
                List<OcrResultEntity> result = ocrResultDao.select(ocrResultEntity);
                if (result == null || result.size() < 1) {
                    continue;
                }
                String ocrResult = result.get(0).getResult();
                List<String> list = getOcrResult(ocrResult);
                if (null == list) {
                    continue;
                }
                ocrResultList.addAll(list);
            }
            // 如果无识别文字，则导出空文档
            if (ocrResultList == null || ocrResultList.size() < 1) {
                ocrResultList.add(" ");
            }
            download(response, ocrResultList, fileName);
        } else {
            OcrResultEntity ocrResultEntity = new OcrResultEntity();
            ocrResultEntity.setFileId(ocrFileResult.getId());
            List<OcrResultEntity> result = ocrResultDao.select(ocrResultEntity);
            String ocrResult = result.get(0).getResult();
            List<String> list = getOcrResult(ocrResult);
            if (list == null || list.size() < 1) {
                list.add(" ");
            }
            download(response, list, fileName);
        }
    }

    // 解析json结果集
    public List<String> getOcrResult(String resultJson) {
        List<String> ocrResultList = new ArrayList<>();
        JSONObject jSONObject = JSONObject.parseObject(resultJson);
        if (jSONObject == null) {
            ocrResultList.add(" ");
            return ocrResultList;
        }
        // 获取表格区的识别结果
        JSONArray tableArray = jSONObject.getJSONArray("Table");
        if (tableArray != null) {
            for (int i = 0; i < tableArray.size(); i++) {
                JSONArray cellArray = tableArray.getJSONObject(i).getJSONArray("cell");
                if (cellArray == null) {
                    continue;
                }
                for (int j = 0; j < cellArray.size(); j++) {
                    JSONArray textlineArray = cellArray.getJSONObject(j).getJSONArray("textline");
                    if (textlineArray == null) {
                        continue;
                    }
                    for (int k = 0; k < textlineArray.size(); k++) {
                        JSONArray sentArray = textlineArray.getJSONObject(k).getJSONArray("sent");
                        if (sentArray == null) {
                            continue;
                        }
                        for (int m = 0; m < sentArray.size(); m++) {
                            String ocrValue = sentArray.getJSONObject(m).getString("value");
                            if (null == ocrValue) {
                                continue;
                            }
                            ocrResultList.add(ocrValue);
                        }
                    }
                }
            }
        }
        // 获取文本区的识别结果
        JSONArray textRegionArray = jSONObject.getJSONArray("TextRegion");
        if (textRegionArray != null) {
            for (int i = 0; i < textRegionArray.size(); i++) {
                JSONArray textlineArray = textRegionArray.getJSONObject(i).getJSONArray("textline");
                if (textlineArray == null) {
                    continue;
                }
                for (int j = 0; j < textlineArray.size(); j++) {
                    JSONArray sentArray = textlineArray.getJSONObject(j).getJSONArray("sent");
                    if (sentArray == null) {
                        continue;
                    }
                    for (int k = 0; k < sentArray.size(); k++) {
                        String ocrValue = sentArray.getJSONObject(k).getString("value");
                        if (null == ocrValue) {
                            continue;
                        }
                        ocrResultList.add(ocrValue);
                    }
                }
            }
        }
        return ocrResultList;
    }

    // 根据数组导出文档
    public void download(HttpServletResponse response, List<String> ocrResultList, String fileName) throws IOException {
        // // 中文括号替换英文括号，滤除空格
        // fileName = fileName.replace('(', '（');
        // fileName = fileName.replace(')', '）');
        // fileName = fileName.replace(" ", "");
        // fileName = URLEncoder.encode(fileName + ".doc", "UTF-8");
        fileName = fileName + ".doc";
        // Pattern pattern = Pattern.compile("([\u4E00-\u9FA5]|[\uFE30-\uFFA0])+");
        Pattern pattern = Pattern.compile("[\u4E00-\u9FA5|\\！|\\,|\\。|\\（|\\）|\\《|\\》|\\“|\\”|\\？|\\：|\\；|\\【|\\】]+");
        Matcher matcher = pattern.matcher(fileName);
        while (matcher.find()) {
            String mStr = matcher.group();
            try {
                fileName = fileName.replaceFirst(mStr, URLEncoder.encode(mStr, "UTF-8"));
            } catch (Exception e) {

            }
        }
        // 获取文件内容并写到doc格式里
        String deFilepathString = "";
        // 区分系统目录
        String os = System.getProperty("os.name");
        if (os.toLowerCase().startsWith("win")) {
            deFilepathString = System.getProperty("user.dir") + "\\doc\\templet.doc";
        } else {
            deFilepathString = System.getProperty("user.dir") + "/../doc/templet.doc";
        }
        InputStream is = new FileInputStream(deFilepathString);
        HWPFDocument doc = new HWPFDocument(is);
        StringBuffer stringBuffer = new StringBuffer();
        for (String word : ocrResultList) {
            stringBuffer.append(word + "\r");
        }
        doc.getRange().insertBefore(stringBuffer.toString());
        // 系统类型判断
        File fileTemp = new File(FileUtils.getTempDirectory(), UUIDUtil.getUUID() + ".doc");
        OutputStream outputStreamTemp = new FileOutputStream(fileTemp);
        // 把doc输出到输出流中
        doc.write(outputStreamTemp);
        doc.close();

        // 通过respnose返回下载
        response.setContentType("application/x-msdownload;");
        response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
        response.getOutputStream().write(FileUtils.readFileToByteArray(fileTemp));
        response.getOutputStream().flush();
        response.getOutputStream().close();
        fileTemp.delete();
    }

    @Override
    public Integer writeFilesTo(List<String> fileIds, ZipOutputStream zippedOut) {
        Example example = new Example(OcrFileEntity.class);
        Criteria criteria = example.createCriteria();
        criteria.andIn("id", fileIds);
        List<OcrFileEntity> filesInDB = ocrFileDao.selectByExample(example);

        if (CollectionUtils.isEmpty(filesInDB)) {
            return null;
        }

        int sumSize = 0;
        for (OcrFileEntity fileEntity : filesInDB) {
            ZipEntry e = new ZipEntry(fileEntity.getFileName() + "." + fileEntity.getFileType());
            // Configure the zip entry, the properties of the file
            e.setSize(fileEntity.getFileSize());
            e.setTime(fileEntity.getCreateTime().getTime());
            // etc.
            try {
                zippedOut.putNextEntry(e);

                // And the content of the resource:
                try {
                    //storageClient.downloadFile(zippedOut, fileEntity.getFilePath());
                    byte[] btFile = PoliceDataExchangeUtils.downloadFile(fdfsUrl + "/" + fileEntity.getFilePath(), "", "");
                    
                    zippedOut.write(btFile, 0, btFile.length);
                } catch (Exception e1) {
                    logger.warn("下载 fastDFS 文件异常. filePath: {}", fileEntity.getFilePath(), e);
                }

                sumSize += Math.toIntExact(fileEntity.getFileSize());
                zippedOut.closeEntry();
            } catch (IOException e1) {
                logger.warn("组装压缩文件异常. fileId: {}, fileName: {}", fileEntity.getId(), fileEntity.getFileName(), e);
            }
        }

        return sumSize;
    }

    @Override
    public PageResult<FileInfo> search(String userId, String dirId, String keyword, boolean byName, boolean byText,
                                       Integer page, Integer size) {
        PageResult<FileInfo> pageResult = new PageResult<>(page, size);

        if (!byName && !byText) {
            return pageResult;
        }

        if (byName && byText) {
            PageHelper.startPage(page, size);
            List<Map<String, Object>> resultEntities = ocrResultDao.findByText(userId, dirId, keyword,FILE_DELETE, true);
            pageResult.setTotal(((Page) resultEntities).getTotal());
            pageResult.setData(convertToFileInfoFromOcrResult(resultEntities, keyword));
        } else if (byText) {
            PageHelper.startPage(page, size);
            List<Map<String, Object>> resultEntities = ocrResultDao.findByText(userId, dirId, keyword,FILE_DELETE,false);
            pageResult.setTotal(((Page) resultEntities).getTotal());
            pageResult.setData(convertToFileInfoFromOcrResult(resultEntities, keyword));
        } else {
        	//先查询示例文件夹
        	Example e = new Example(OcrDirectoryEntity.class);
        	e.createCriteria().andEqualTo("userId", userId).andEqualTo("dirType", CommonConstant.DIR_TYPE_EXAMPLE);
        	OcrDirectoryEntity ocrDirectoryEntity=ocrDirectoryDao.selectOneByExample(e);
        	String exampleDirId=StringUtils.EMPTY;
        	if(null!=ocrDirectoryEntity) {
        		exampleDirId=ocrDirectoryEntity.getId();
        	}
            PageHelper.startPage(page, size);
            Example example = new Example(OcrFileEntity.class);
            //example.createCriteria().andLike("fileName", "%" + keyword + "%").andIsNull("parentId").andEqualTo("userId", userId).andEqualTo("dirId", dirId);
            Criteria c=example.createCriteria().andCondition("binary file_name like '%" + keyword + "%'")
            .andIsNull("parentId").andEqualTo("userId", userId).andEqualTo("isDelete",FILE_DELETE).andIsNotNull("dirId");
            if(StringUtils.isNotEmpty(exampleDirId)) {
            	c.andNotEqualTo("dirId", exampleDirId);
            }
            example.orderBy("createTime").desc();
            List<OcrFileEntity> fileEntities = ocrFileDao.selectByExample(example);
            pageResult.setData(convertToFileInfoFromOcrFile(fileEntities, keyword));
            pageResult.setTotal(((Page) fileEntities).getTotal());
        }

        return pageResult;
    }

    private List<FileInfo> convertToFileInfoFromOcrResult(List<Map<String, Object>> resultEntities, String keyword) {
        ArrayList<FileInfo> fileInfos = new ArrayList<>();
        if (CollectionUtils.isEmpty(resultEntities)) {
            return fileInfos;
        }

        // 查询父文档文件名
        List<String> fileIds = resultEntities.stream()
                .map(e -> String.valueOf(e.getOrDefault("groupId", e.get("fileId"))))
                .collect(Collectors.toList());
        List<Map<String,String>> listMap=queryFIleNames(fileIds);
        Map<String, String> nameMap=listMap.get(0);
        Map<String, String> fileTypeMap=listMap.get(1);
        Map<String, String> canEditMap=listMap.get(2);
        // 结果类型转换
        for (Map<String, Object> entity : resultEntities) {
            FileInfo fileInfo = new FileInfo();
            String id = String.valueOf(entity.getOrDefault("groupId", entity.get("fileId")));
            fileInfo.setId(id);
            String fileName = nameMap.getOrDefault(id, id);
            String fileType = fileTypeMap.getOrDefault(id, "");
            boolean canEdit=Boolean.valueOf(String.valueOf(canEditMap.getOrDefault(id, "false")));
            fileInfo.setFileName(fileName);
            fileInfo.setFileType(fileType);
            fileInfo.setCanEdit(canEdit);
            // 高亮匹配标题
            String highlightName = StringUtils
                    .replace(fileInfo.getFileName(), keyword, String.format(HIGHLIGHT_PATTERN, keyword));
            fileInfo.setHighlightName(highlightName);

            // 高亮匹配结果, 截取 2个字符+匹配结果+n个字符=20个字符
            String text = String.valueOf(entity.get("text"));
            int keywordIndex = StringUtils.indexOf(text, keyword);
            if (keywordIndex != -1) {
                int start = Math.max(keywordIndex - 2, 0);
                String subText = StringUtils.substring(text, start, start + 20);
                String highlightText = StringUtils.replace(subText, keyword, String.format(HIGHLIGHT_PATTERN, keyword));
                fileInfo.setHighlightText(highlightText);
            }

            Object createTime = entity.get("createTime");
            if (createTime instanceof Date) {
                fileInfo.setCreateTime((Date) entity.get("createTime"));
            }
            if("pdf".equalsIgnoreCase(fileType)) {//PDF分割的图片
            	String fileId=String.valueOf(entity.get("subFileId"));
            	int subFilePage=Integer.valueOf(String.valueOf(entity.get("subFilePage")));
            	fileInfo.setSubFileId(fileId);
            	fileInfo.setSubFilePage(subFilePage);
            }

            fileInfos.add(fileInfo);
        }

        return fileInfos;
    }

	private List<Map<String, String>> queryFIleNames(List<String> fileIds) {
		Example example = new Example(OcrFileEntity.class);
		example.selectProperties("id", "fileName", "fileType");
		example.createCriteria().andIn("id", fileIds);
		List<OcrFileEntity> nameEntities = ocrFileDao.selectByExample(example);
		if (CollectionUtils.isEmpty(nameEntities)) {
			return new ArrayList<Map<String, String>>();
		}
		Map<String, String> canEditMap = new HashMap<String, String>();
		for (OcrFileEntity entity : nameEntities) {
			String dirId = entity.getDirId();
			if (StringUtils.isNotEmpty(dirId)) {
				OcrDirectoryEntity ocrDirectoryEntity = ocrDirectoryDao
						.selectByPrimaryKey(dirId);
				Short dirType = ocrDirectoryEntity.getDirType();
				if (0 == dirType) {
					canEditMap.put(entity.getId(), "false");
				} else {
					canEditMap.put(entity.getId(), "true");
				}

			} else {
				canEditMap.put(entity.getId(), "true");
			}
		}
		Map<String, String> nameMap = new HashMap<String, String>();
		Map<String, String> fileTypeMap = new HashMap<String, String>();
		Stream<OcrFileEntity> nameStream = nameEntities.stream();
		Stream<OcrFileEntity> fileTypeStream = nameEntities.stream();
		nameMap = nameStream.collect(
				Collectors.toMap(e -> e.getId(), e -> e.getFileName()));
		fileTypeMap = fileTypeStream.collect(
				Collectors.toMap(e -> e.getId(), e -> e.getFileType()));
		List<Map<String, String>> listMap = new ArrayList<Map<String, String>>();
		listMap.add(nameMap);
		listMap.add(fileTypeMap);
		listMap.add(canEditMap);
		return listMap;
	}


    private List<FileInfo> convertToFileInfoFromOcrFile(List<OcrFileEntity> fileEntities, String keyword) {
        ArrayList<FileInfo> fileInfos = new ArrayList<>();
        if (CollectionUtils.isEmpty(fileEntities)) {
            return fileInfos;
        }

        for (OcrFileEntity fileEntity : fileEntities) {
            FileInfo fileInfo = convert(fileEntity);
            // 高亮匹配结果
            String highlightName = StringUtils
                    .replace(fileInfo.getFileName(), keyword, String.format(HIGHLIGHT_PATTERN, keyword));
            fileInfo.setHighlightName(highlightName);
            fileInfos.add(fileInfo);
        }

        return fileInfos;
    }

    private FileInfo convert(OcrFileEntity entity) {
        if (entity == null) {
            return null;
        }

        FileInfo fileInfo = new FileInfo();
        fileInfo.setDirId(entity.getDirId());
        fileInfo.setFileName(entity.getFileName());
        Integer filePage = entity.getFilePage();
        if (filePage != null) {
            fileInfo.setFilePage(filePage);
        }
        fileInfo.setFilePath(entity.getFilePath());
        Long fileSize = entity.getFileSize();
        if (fileSize != null) {
            fileInfo.setFileSize(fileSize);
        }
        fileInfo.setFileType(entity.getFileType());
        fileInfo.setFormatPath(entity.getFormatPath());
        Long formatSize = entity.getFormatSize();
        if (formatSize != null) {
            fileInfo.setFormatSize(formatSize);
        }
        fileInfo.setFormatType(entity.getFormatType());
        fileInfo.setId(entity.getId());
        fileInfo.setParentId(entity.getParentId());
        Integer status = entity.getStatus();
        if (status != null) {
            fileInfo.setStatus(status);
        }
        fileInfo.setUserId(entity.getUserId());
        fileInfo.setCreateTime(entity.getCreateTime());

        return fileInfo;
    }

    @Override
    public boolean markOpened(String fileId) {
        OcrFileEntity param = new OcrFileEntity();
        param.setId(fileId);
        param.setStatus(FileStatusEnum.OPENED.getCode());
        return ocrFileDao.updateByPrimaryKeySelective(param) == 1;
    }

    @Override
    public ExportFileInfo exportToFile(String fileId, String type) {
        OcrFileEntity queryEntity = new OcrFileEntity();
        queryEntity.setId(fileId);
        OcrFileEntity fileEntity = ocrFileDao.selectOne(queryEntity);
        if (null == fileEntity) {
            logger.info("文件不存在. fileId: {}", fileId);
            return null;
        }

        // 文件有多页
        //String parentId = fileEntity.getParentId(); //TODO
        if (fileEntity.getFileType().equalsIgnoreCase("pdf")) {
            fileEntity.setParentId(fileId);
            return exportMultiPageFile(fileEntity, type);
        }

        return exportSinglePageFile(fileEntity, type);
    }

    @Override
    public boolean exportCheck(String fileId,String type) {
        OcrFileEntity ocrFileEntity = new OcrFileEntity();
        ocrFileEntity.setId(fileId);
        List<OcrFileEntity> files = ocrFileDao.select(ocrFileEntity);
        if (CollectionUtils.isNotEmpty(files) && !files.get(0).getFileType().equalsIgnoreCase(Constants.SupportExt.PDF)) //不是pdf 并且有文件
        {
        	//查询识别结果是否为空
            Example example = new Example(OcrResultEntity.class);
            Criteria criteria=example.createCriteria().andEqualTo("fileId",fileId).andIsNotNull("result");
            if(StringUtils.isNotEmpty(type) && Constants.SupportExt.XLSX.equalsIgnoreCase(type)) {//不是pdf并且导出类型excel，必须要添加html条件判断
            	criteria.andIsNotNull("html");
            }
    		// 查询识别结果
    		int resutCnt= ocrResultDao.selectCountByExample(example);
    		if(resutCnt>0) {
    			return true;
    		}
    		return false;
        }
        /*if(StringUtils.isNotEmpty(type) && Constants.SupportExt.XLSX.equalsIgnoreCase(type) && files.get(0).getFilePage() == 1) {//判断pdf是否能导出excel文件
        	logger.info("PDF在只有一页并且html有值的情况下，支持导出excel文件. fileId: {}", fileId);
            Example example = new Example(OcrResultEntity.class);
            Criteria criteria=example.createCriteria().andEqualTo("fileId",fileId).andIsNotNull("result").andIsNotNull("html");
            int resutCnt= ocrResultDao.selectCountByExample(example);
            if (resutCnt > 0){
                return true;
            }else {
                return false;
            }
        }*/
        //多页pdf导出excel直接返回false，即（不允许这种操作）
        if (StringUtils.isNotEmpty(type) && Constants.SupportExt.XLSX.equalsIgnoreCase(type) && files.get(0).getFilePage() != 1){
            return false;
        }
        //查询识别结果是否为空
        Example example = new Example(OcrResultEntity.class);
		Criteria pdfCriteria = example.createCriteria().andEqualTo("groupId",fileId).andIsNotNull("result");
		//当只有一页pdf并且导出为excel文件时，需要添加过滤条件

		/*if (StringUtils.isNotEmpty(type) && Constants.SupportExt.XLSX.equalsIgnoreCase(type) && files.get(0).getFilePage() == 1){
            pdfCriteria.andIsNotNull("html");
        }*/
		int ocrResultEntitiesNum = ocrResultDao.selectCountByExample(example);//查询识别的页数
        
        //OcrFileEntity parentFileEntity = new OcrFileEntity();
       // parentFileEntity.setParentId(fileId);
        //int fileEntityNum = ocrFileDao.selectCount(parentFileEntity);//查询拆分图片的页数

        //是pdf 文件数量正常
       // return fileEntityNum != 0 && ocrResultEntitiesNum == fileEntityNum && ocrResultEntitiesNum == files.get(0).getFilePage();
        return  ocrResultEntitiesNum == files.get(0).getFilePage();
    }

    /**
     * 导出单页文件
     */
    private ExportFileInfo exportSinglePageFile(OcrFileEntity fileEntity, String type) {
        try {
            OcrResultEntity paramEntity = new OcrResultEntity();
            paramEntity.setFileId(fileEntity.getId());
            List<OcrResultEntity> ocrResultEntities = ocrResultDao.select(paramEntity);
            if (CollectionUtils.isEmpty(ocrResultEntities)) {
                logger.info("没有 ocr 识别结果");
                return null;
            }
            //String ocrResult = ocrResultEntities.get(0).getResult();
            //String filePath = fileEntity.getFilePath();
            //byte[] bytes = this.getFileBytes(filePath);

            List<String> ocrResultList = new ArrayList<>();//识别json集合

            //不走网络
            List<com.iflytek.icourt.model.pdf.Page> pageListParam=new ArrayList<>();
            byte[] fileBytes=null;

            if (!type.equalsIgnoreCase("pdf")) {
                FileExportStrategy fileExportStrategy = determineExporter(type);
                if (fileExportStrategy == null) {
                    logger.info("不支持该导出类型. exportType: {}", type);
                    return null;
                }
                String ocrResultJsonStr="{\"task_result\":{\"page\":[" + ocrResultEntities.get(0).getResult() + "]}}";
                String pureJson = JSON.toJSONString(JSON.parseObject(ocrResultJsonStr));
                OcrResult ocrResult=JSONObject.parseObject(pureJson, OcrResult.class);
                List<com.iflytek.icourt.model.pdf.Page> pageList = ocrResult.getTaskResult().getPage();
                pageListParam.addAll(pageList);
                //word字号
                List<String> exportParam = new ArrayList<>();
                exportParam.add(exportProperties.getWordRtfSize());
                //exportParam.add(exportProperties.getWordFamilyName());
                fileBytes= fileExportStrategy.createFileByOcrResultPageList(pageListParam, type,exportParam);

                ExportFileInfo exportFileInfo = new ExportFileInfo();
                exportFileInfo.setFileName(fileEntity.getFileName() + "." + type);
                exportFileInfo.setContent(fileBytes);
                return exportFileInfo;
            }
            if (type.equalsIgnoreCase("pdf")) {
                List<byte[]> filesBytes = new ArrayList<>(); //图片集合
                byte[] bytes = this.getFileBytes(fileEntity.getFilePath());
                String fileType = fileEntity.getFileType();
                if ("tif".equals(fileType) || "tiff".equals(fileType)) {
                    // 格式转换
                    logger.debug("------- 开始图片格式转换-------");
                    ByteArrayOutputStream jpgStream = (ByteArrayOutputStream) ImageUtil
                            .tiff2Jpg(new ByteArrayInputStream(bytes));
                    if (jpgStream != null) {
                        bytes = jpgStream.toByteArray();
                    }
                }

                filesBytes.add(bytes);// pdf 文档需要原始图片作为底层
                ocrResultList.add("{\"task_result\":{\"page\":[" + ocrResultEntities.get(0).getResult() + "]}}");
                fileBytes= epxortPdf(filesBytes, ocrResultList);
                ExportFileInfo exportFileInfo = new ExportFileInfo();
                exportFileInfo.setFileName(fileEntity.getFileName() + "." + type);
                exportFileInfo.setContent(fileBytes);
                return exportFileInfo;
            	
            }
            return null;


            /*Map<String, Object> body = new HashMap<>();
            body.put("type", type);

            if (type.equalsIgnoreCase("pdf")) {
                List<String> filesBytes = new ArrayList<>();

                filesBytes.add(JSONArray.toJSONString(bytes));   // pdf 文档需要原始图片作为底层
                body.put("fileBytes", filesBytes);
            }
            ocrResultList.add("{\"task_result\":{\"page\":[" + ocrResult + "]}}");
            body.put("ocrResult", ocrResultList);

            byte[] fileBytes = requestExport(body);

            ExportFileInfo exportFileInfo = new ExportFileInfo();
            exportFileInfo.setFileName(fileEntity.getFileName() + "." + type);
            exportFileInfo.setContent(fileBytes);
            return exportFileInfo;*/
        } catch (Exception ex) {
            return null;
        }
    }

    private byte[] requestExport(Map<String, Object> body) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpEntity<String> request = new HttpEntity<String>(JSONUtil.toStrByDefault(body), headers);
        String requestUrl = StringUtils.appendIfMissing(cosmoUrl, "/") + "iflytek/ocr/view/export";
        try {
            String respStr = restTemplate.postForObject(requestUrl, request, String.class);
            if (StringUtils.isBlank(respStr)) {
                logger.info("ocr 导出接口返回空");
                return null;
            }
            return JSON.parseObject(respStr).getBytes("data");
        } catch (Exception e) {
            logger.info("请求ocr结果导出接口异常. url: {}", requestUrl, e);
            return null;
        }
    }
    /**
     *导出html 或rtf 文档
     */
    @Override
    public ExportFileHtml exportToHtml(String fileId, String type) {
        OcrFileEntity queryEntity = new OcrFileEntity();
        queryEntity.setId(fileId);
        OcrFileEntity fileEntity = ocrFileDao.selectOne(queryEntity);

        String parentId = fileEntity.getParentId();//没有识别完成 返回提示告知当前导出多少页

        OcrResultEntity paramEntity = new OcrResultEntity();
        paramEntity.setGroupId(fileId);
        List<OcrResultEntity> resultEntityList = ocrResultDao.select(paramEntity);

        List<OcrFileEntity> fileEntities = ocrFileDao.selectSubFile(fileId);

        List<String> sorteFileIds = fileEntities.stream().sorted(Comparator.comparingInt(OcrFileEntity::getFilePage))
                .map(OcrFileEntity::getId).collect(Collectors.toList());
        List<String> sortedResultEntities = new ArrayList<>();
        Map<String, OcrResultEntity> fileIdResult = resultEntityList.stream().collect(
                Collectors.toMap(e -> e.getFileId(), e -> e));
        for (String file_id : sorteFileIds) {
            sortedResultEntities.add(fileIdResult.get(file_id).getText());
        }

//        List<OcrResult> ocrResults = new ArrayList<>();
//        for (OcrResultEntity entity : sortedResultEntities) {
//            String ocrResultJsonStr = "{\"task_result\":{\"page\":[" + entity.getResult() + "]}}";
//            String pureJson = JSON.toJSONString(JSON.parseObject(ocrResultJsonStr));
//            OcrResult ocrResult = JSONObject.parseObject(pureJson, OcrResult.class);
//            ocrResults.add(ocrResult);
//        }
//        List<String> chars = ocrResults.stream().map(r -> r.getTaskResult().getPage()).flatMap(p -> p.stream())
//                .map(FileExportUtil::getCharListByPage).flatMap(l -> l.stream()).map(ch -> ch.getValue()).collect(Collectors.toList());
        String join = StringUtils.join(sortedResultEntities, "");
        logger.info("导出内容是："+join);
        ExportFileHtml exportFileHtml = new ExportFileHtml();
        exportFileHtml.setFileName(fileEntity.getFileName());
        exportFileHtml.setOcrResult(sortedResultEntities);
        return exportFileHtml;
    }

    /**
     * 导出多页文件
     */
    private ExportFileInfo exportMultiPageFile(OcrFileEntity fileEntity, String type) {
    	try {
	    	String parentId = fileEntity.getParentId();//没有识别完成 返回提示告知当前导出多少页
	
	    	OcrResultEntity paramEntity = new OcrResultEntity();
	        paramEntity.setGroupId(parentId);
	        List<OcrResultEntity> resultEntityList = ocrResultDao.select(paramEntity);

	        List<OcrFileEntity> fileEntities = ocrFileDao.selectSubFile(parentId);

	        List<String> sorteFileIds = fileEntities.stream().sorted(Comparator.comparingInt(OcrFileEntity::getFilePage))
	                .map(OcrFileEntity::getId).collect(Collectors.toList());
	        List<OcrResultEntity> sortedResultEntities = new ArrayList<>();
	        Map<String, OcrResultEntity> fileIdResult = resultEntityList.stream().collect(
                    Collectors.toMap(e -> e.getFileId(), e -> e));
	        for (String fileId : sorteFileIds) {
	            sortedResultEntities.add(fileIdResult.get(fileId));
	        }
	        //ocr结果到处不走网络，直接使用工具类
	        
	        List<String> ocrResultList = new ArrayList<>();//识别json集合

	        byte[] fileBytes=null;
	        //pdf拆成几百张图片这里组装成String ocrResults会内存溢出
	        if (!type.equalsIgnoreCase("pdf")) {
	        	FileExportStrategy fileExportStrategy = determineExporter(type);
	        	if (fileExportStrategy == null) {
		            logger.info("不支持该导出类型. exportType: {}", type);
		            return null;
		        }

                //word字号
                List<String> exportParam = new ArrayList<>();
                exportParam.add(exportProperties.getWordRtfSize());
                List<OcrResult> ocrResults = new ArrayList<>();
                for (OcrResultEntity entity : sortedResultEntities) {
                    String ocrResultJsonStr = "{\"task_result\":{\"page\":[" + entity.getResult() + "]}}";
                    String pureJson = JSON.toJSONString(JSON.parseObject(ocrResultJsonStr));
                    OcrResult ocrResult = JSONObject.parseObject(pureJson, OcrResult.class);
                    ocrResults.add(ocrResult);
                }
                List<Char> chars = ocrResults.stream().map(r -> r.getTaskResult().getPage()).flatMap(p -> p.stream())
                                               .map(FileExportUtil::getCharListByPage).flatMap(l -> l.stream())
                                               .collect(Collectors.toList());
                Integer commonHeight = FileExportUtil.commonHeight(chars);
                exportParam.add(String.valueOf(commonHeight == null ? 36 : commonHeight));

                List<byte[]> pageBytesList = ocrResults.parallelStream().map(r -> r.getTaskResult().getPage())
                          .map(pages -> fileExportStrategy.createFileByOcrResultPageList(pages, type, exportParam))
                          .filter(ArrayUtils::isNotEmpty).collect(Collectors.toList());


                if ("txt".equalsIgnoreCase(type)) {
                    List<com.iflytek.icourt.model.pdf.Page> pageListParam = new ArrayList<>();
                    for (OcrResult ocrResult : ocrResults) {
                        List<com.iflytek.icourt.model.pdf.Page> pageList = ocrResult.getTaskResult().getPage();
                        pageListParam.addAll(pageList);
                    }
                    fileBytes = fileExportStrategy.createFileByOcrResultPageList(pageListParam, type, exportParam);
                }
                else {
                    // 子页面文件合并
                    XWPFDocument first = null;
                    for (int i = 0; i < pageBytesList.size(); i++) {
                        try (ByteArrayInputStream inputStream = new ByteArrayInputStream(pageBytesList.get(i))) {
                            OPCPackage srcPackage = OPCPackage.open(inputStream);
                            XWPFDocument src1Document = new XWPFDocument(srcPackage);
                            src1Document.createParagraph().setPageBreak(true);
                            if (i == 0) {
                                first = src1Document;
                            }
                            else {
                                CTBody srcBody = src1Document.getDocument().getBody();
                                first.getDocument().addNewBody().set(srcBody);
                            }
                        }
                        catch (Exception e) {
                            logger.warn("合并子页面异常, filename: {}, index: {}", fileEntity.getFileName(), i, e);
                        }
                    }
                }
                ExportFileInfo exportFileInfo = new ExportFileInfo();
		        exportFileInfo.setFileName(fileEntity.getFileName() + "." + type);
		        exportFileInfo.setContent(fileBytes);
//                IOUtils.write(fileBytes, new FileOutputStream("/Users/tonnyyi/Downloads/demo/merge.docx"));
		        return exportFileInfo;
	        }
	        if (type.equalsIgnoreCase("pdf")) {
	            List<byte[]> filesBytes = new ArrayList<>(); //图片集合
	            for (OcrFileEntity ocrFileEntity : fileEntities) {
	                byte[] bytes = this.getFileBytes(ocrFileEntity.getFilePath());
	                filesBytes.add(bytes);// pdf 文档需要原始图片作为底层
	            }
	            for (OcrResultEntity item : sortedResultEntities) {
	                ocrResultList.add("{\"task_result\":{\"page\":[" + item.getResult() + "]}}");
	            }
	            fileBytes= epxortPdf(filesBytes, ocrResultList);
	            ExportFileInfo exportFileInfo = new ExportFileInfo();
	            exportFileInfo.setFileName(fileEntity.getFileName() + "." + type);
	            exportFileInfo.setContent(fileBytes);
	            return exportFileInfo;
	        }
	        return null;
	       
	    } catch (Exception ex) {
            return null;
        }
    }

    private byte[] epxortPdf(List<byte[]> fileBytes, List<String> ocrResult) {
        if (fileBytes == null) {
            logger.info("pdf 导出失败, 原始文件字节参数为空.");
            return null;
        }

        try {
            List<byte[]> pdf = new ArrayList<byte[]>();

            final Map<Integer,byte[]>  pdfMap = new ConcurrentHashMap<>();
            //改成多线程实现pdf单页实现
            int size = Math.min(ocrResult.size(), fileBytes.size());
            final CountDownLatch countDownLatch = new CountDownLatch(size);

            for (int i = 0; i < size; i++) {
                final int threadNum = i;
                PDF_CREATE_THREAD_POOL.execute(() -> {
                    try {
                        pdfMap.put(threadNum,new PdfComposeUtil().handleCompose(fileBytes.get(threadNum), ocrResult.get(threadNum)));
                        //pdf.add();
                    } catch (Exception e) {
                        logger.warn("导出 PDF 单页文件异常. 异常消息: {}", e);
                    } finally {
                        countDownLatch.countDown();
                    }
                });
            }
            countDownLatch.await();
            Map<Integer,byte[]> result = new LinkedHashMap<>();
            //排序
            pdfMap.entrySet().stream()
                    .sorted(Map.Entry.comparingByKey())
                    .forEachOrdered(x -> result.put(x.getKey(), x.getValue()));
            //转换成list
            pdf = result.values().stream()
                    .collect(Collectors.toList());
            if (pdf.size() == 0) {
            	return null;
            }
            if (pdf.size() == 1) return pdf.get(0);
            return new PdfComposeUtil().mergePdfList(pdf);
        } catch (Exception e) {
            logger.warn("导出 PDF 文件异常. 异常消息: {}", e);
            return null;
        }
    }

    private FileExportStrategy determineExporter(String exportType) {
        switch (exportType) {
            case "txt":
                return txtFileExportStrategy;
            case "rtf":
            case "docx":
            case "wps":
            case "html":
                return rtfDocxHtmlFileExportStrategy;
            case "xlsx":
            case "et":
            case "csv":
                return xlsxCsvFileExportStrategy;
            default:
                logger.warn("不支持该导出文件格式");
        }
        return null;
    }

	@Override
	public List<OcrDirectoryEntity> getAllDirectory(String userId, HttpServletRequest request) {
		Example example = new Example(OcrDirectoryEntity.class);
		example.setOrderByClause("DIR_TYPE ASC,CREATE_TIME asc");
		example.createCriteria().andEqualTo("userId", userId).andEqualTo("isDelete",FILE_DELETE);
		// 查询所有目录
		List<OcrDirectoryEntity> list = ocrDirectoryDao.selectByExample(example);
		// 新用户系统创建我的文件夹+示例文件夹
		if (CollectionUtils.isEmpty(list)) {
			OcrDirectoryEntity ocrDirectoryEntity = new OcrDirectoryEntity();
			ocrDirectoryEntity.setId(UUIDUtil.getUUID());
			ocrDirectoryEntity.setCreateTime(new Date());
			ocrDirectoryEntity.setDirName(CommonConstant.DIR_TYPE_DEFAULT_NAME);
			ocrDirectoryEntity.setDirType(CommonConstant.DIR_TYPE_DEFAULT);
			ocrDirectoryEntity.setUserId(userId);
			ocrDirectoryDao.createDirectory(ocrDirectoryEntity);

			String exampleDirId = UUIDUtil.getUUID();
			ocrDirectoryEntity.setId(exampleDirId);
			ocrDirectoryEntity.setCreateTime(new Date());
			ocrDirectoryEntity.setDirName(CommonConstant.DIR_TYPE_EXAMPLE_NAME);
			ocrDirectoryEntity.setDirType(CommonConstant.DIR_TYPE_EXAMPLE);
			ocrDirectoryEntity.setUserId(userId);
			ocrDirectoryDao.createDirectory(ocrDirectoryEntity);
			list = ocrDirectoryDao.selectByExample(example);
		}
		list.parallelStream().forEach((entity) -> {
			Short dirType = entity.getDirType();
			if (dirType == CommonConstant.DIR_TYPE_COMMON) {
				entity.setCanEdit(true);
			}
			if (dirType == CommonConstant.DIR_TYPE_EXAMPLE) {
				String exampleDirId = entity.getId();
				Example fileExample = new Example(OcrFileEntity.class);
				fileExample.createCriteria().andEqualTo("dirId", exampleDirId).andEqualTo("isDelete",FILE_DELETE);
				int cnt = ocrFileDao.selectCountByExample(fileExample);
				List<MultipartFile> files=null;
				// 没有示例文件则上传
				if (cnt == 0) {
					files = exampleFileByteConfig.getExampleFiles();
				}else {
					List<MultipartFile> configfiles = exampleFileByteConfig.getExampleFiles();
					if(cnt !=configfiles.size()) {
						List<OcrFileEntity> existsFiles= ocrFileDao.selectByExample(fileExample);
						//求差集
						files = configfiles.stream()
						        .filter(item -> !existsFiles.stream().map(e -> e.getFileName()+"."+e.getFileType())
						        .collect(Collectors.toList())
						        .contains(item.getOriginalFilename()))
						        .collect(Collectors.toList());
						
					}
				}
				if(CollectionUtils.isNotEmpty(files)) {
					// 往示例文件夹中上传示例文件
					String ip = IpAddress.getRemoteIpAddr(request);
					for(MultipartFile multipartFile:files) {
                        MessageOutput<String> out = TaskDispatchSystem.getInstance(worksProperties)
                                                                      .addTask(userId, exampleDirId, multipartFile, ip);
						if(out.getState().getOk() == CommonConstant.SUCCESS_CODE) {
							logger.info("上传示例文件{}成功", multipartFile.getOriginalFilename());
						}else {
							logger.info("上传示例文件{}失败,失败原因{}", multipartFile.getOriginalFilename(),out.getBody());
						}
					}
				}
			}
		});
		return list;
	}

	@Override
	public String queryDistinctDirName(List<String> fileIds) {
		String dirName = null;
		Example example = new Example(OcrFileEntity.class);
		Criteria criteria = example.createCriteria();
		criteria.andIn("id", fileIds);
		List<OcrFileEntity> list = ocrFileDao.selectByExample(example);
		Set<OcrFileEntity> set = list.stream()
				.filter(distinctByKey(b -> b.getDirId()))
				.collect(Collectors.toSet());
		if (set.size() > 1) {
			return dirName;
		}
		Iterator<OcrFileEntity> it = set.iterator();
		if (it.hasNext()) {
			String dirId = it.next().getDirId();

			OcrDirectoryEntity queryOcrDirectoryEntity = new OcrDirectoryEntity();
			queryOcrDirectoryEntity.setId(dirId);
			OcrDirectoryEntity ocrDirectoryEntity = ocrDirectoryDao
					.selectOne(queryOcrDirectoryEntity);
			dirName = ocrDirectoryEntity.getDirName();
		}
		return dirName;
	}

	private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object,Boolean> seen = new ConcurrentHashMap<Object,Boolean>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

	@Override
	public Integer writeExportFileInfoTo(List<ExportFileInfo> fileList,
			ZipOutputStream zippedOut) {
		int sumSize = 0;
		//导出的zip文件文件重名处理
		Map<String,Integer> nameMap=new HashMap<String, Integer>();
		//for (ExportFileInfo exportFileInfo : fileList) {
			for (int i=fileList.size()-1;i>=0;i--) {
			ExportFileInfo exportFileInfo=fileList.get(i);
			String fileName=exportFileInfo.getFileName();
			if(!nameMap.containsKey(fileName)) {
				nameMap.put(fileName, 0);
			}else {
				Integer num=nameMap.get(fileName);
				num=num+1;
				nameMap.put(fileName, num);
				fileName=fileName.substring(0,fileName.lastIndexOf("."))+"("+num+")"+fileName.substring(fileName.lastIndexOf("."),fileName.length());
			}
			ZipEntry e = new ZipEntry(fileName);
			// Configure the zip entry, the properties of the file
			e.setSize(exportFileInfo.getContent().length);
			// e.setTime(fileEntity.getCreateTime().getTime());
			// etc.
			try {
				zippedOut.putNextEntry(e);
				// And the content of the resource:
				// storageClient.downloadFile(zippedOut,
				// fileEntity.getFilePath());
				byte[] btFile = exportFileInfo.getContent();
				zippedOut.write(btFile, 0, btFile.length);

				sumSize += Math.toIntExact(btFile.length);
				zippedOut.closeEntry();
			} catch (IOException e1) {
				logger.warn("组装压缩文件异常.  fileName: {},异常消息{}",
						exportFileInfo.getFileName(), e);
			}
		}
		return sumSize;
	}

	@Override
	public MessageOutput<Object> correctingPic(String fileId, short operType) {
		Map<String,String> map=new HashMap<>();
		map.put("fileId", fileId);
		map.put("operType", String.valueOf(operType));
		try {
			//1歪斜校正&自动旋转 2图像切边
			if(StringUtils.isEmpty(fileId) ) {
				return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE,"参数fileId错误，不允许为空!"), map);
			}
			//1歪斜校正&自动旋转 2图像切边
			if(operType!=1 && operType!=2 && operType!=3 ) {
				return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE,"参数operType错误，应传:1歪斜校正&自动旋转 2图像切边 3分栏切分!"), map);
			}
			Example example = new Example(OcrAdvFuncEntity.class);
			Criteria criteria = example.createCriteria();
			criteria.andEqualTo("fileId", fileId).andEqualTo("opeType", operType);
			List<OcrAdvFuncEntity> filesInDB = ocrAdvFuncDao.selectByExample(example);
			if (CollectionUtils.isNotEmpty(filesInDB)) {
				logger.info("{}图像在表里已存在", fileId);
				OcrAdvFuncEntity entity = filesInDB.get(0);
				String advCompPath = entity.getAdvCompPath();
				if (StringUtils.isNotEmpty(advCompPath)) {
					map.put("imgSrc", advCompPath);
					return new MessageOutput<Object>(new State(CommonConstant.SUCCESS_CODE,"操作成功！"), map);
				}
				String advRecoPath = entity.getAdvRecoPath();
				map.put("imgSrc", advRecoPath);
				return new MessageOutput<Object>(new State(CommonConstant.SUCCESS_CODE,"操作成功！"), map);
			} else {
				logger.info("{}图像在表里不存在，调用图像预处理引擎生成", fileId);
				OcrFileEntity  record=new OcrFileEntity();
				record.setId(fileId);
				OcrFileEntity fileEntity=ocrFileDao.selectOne(record);
				if(null ==  fileEntity) {
					return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE,"文件不存在:fileId="+fileId), map);
				}
				byte[] btFile = PoliceDataExchangeUtils.downloadFile(fdfsUrl + "/" + fileEntity.getFilePath(), "", "");
				if(null == btFile || btFile.length==0) {
					return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE,"下载图片失败！"), map);
				}
				String operation =this.convertOperation(operType);
				String restResult=this.formIfnppService(fileEntity.getFileName(), btFile, fileId, operation);
				
				String storePath=this.extractFilePath(restResult, fileEntity,operType);
				if(StringUtils.isEmpty(storePath)) {
					return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE,"调用图像预处理引擎失败"), map);
				}
				map.put("imgSrc", storePath);
				return new MessageOutput<Object>(new State(CommonConstant.SUCCESS_CODE,"操作成功！"), map);
			}
		} catch (Exception e) {
			e.printStackTrace();
			return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE, "接口异常，异常消息:"+e), map);
		}
	}
	
	/**
     * 调用图像预处理引擎服务生成结果
     *
     * @param fileName 文件名
     * @param bytes    文件内容
     * @return 识别结果
     */
    private String formIfnppService(String fileName, byte[] bytes,String trackId,String operation) throws Exception {

        String cosmoUrl=BeanHelper.getRecognizeConfig().getCosmoUrl();
        RestTemplate restTemplate=BeanHelper.getRestTemplate();
        // 请求路径
        String requestUrl = cosmoUrl + "iflytek/ifnpp/view/file";

        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.set("Accept", "text/plain");
        requestHeaders.set("Content-Type", "multipart/form-data");

        ByteArrayResource resource = new ByteArrayResource(bytes) {
            @Override
            public String getFilename() {
                return fileName;
            }
        };

        MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
        param.add("picFile", resource);
        param.add("name", fileName);
        param.add("trackId", trackId);
        param.add("operation", operation);

        // 发起请求
        logger.debug("开始调用图像预处理服务, url: {}", requestUrl);

        String restResult = StringUtils.EMPTY;
        try {
            long startTime1 = System.currentTimeMillis();
            restResult = restTemplate.postForObject(requestUrl, param, String.class);
            long endTime1 = System.currentTimeMillis();
            logger.info("方法所需要时间：" + (endTime1 - startTime1));
            resource = null;
        } catch (Exception e) {
            logger.error("调用图像预处理服务时出错，错误信息：" + e);
            throw e;
        }

        bytes = null;
        return restResult;
    }
    /**
     * 转换operType
     * <br>
     * 适用场景:	<br>
     * 调用方式:	<br>
     * 业务逻辑说明<br>
     *
     * @param operType
     * @return
     * @autho dgyu
     * @time 2019年10月29日 上午10:22:54
     */
    private String convertOperation(int operType) {
		String operation = StringUtils.EMPTY;
		switch (operType) {
			case 1 :
				operation = CommonConstant.IFNPP_OPERATION_DETECT_SKEW;
				break;
			case 2 :
				operation = CommonConstant.IFNPP_OPERATION_DETECT_BORDER;
				break;
			case 3 :
				operation = CommonConstant.IFNPP_OPERATION_DETECT_OUTSIDE;
				break;
			default :
				break;
		}
		return operation;
	}
    
    /**
     * 提取引擎处理结果 并且上传fastDFS、入库
     * @throws Exception 
     */
    private String extractFilePath(String restResult,OcrFileEntity fileEntity,short opeType) throws Exception {
        String base64Code = StringUtils.EMPTY;
        // 判断rest返回结果是否有值
        if (StringUtils.isNotBlank(restResult)) {
            JsonNode restResultNode = Jackson.parseJsonNode(restResult);
            boolean successValue = restResultNode.get("success").booleanValue();
            if (successValue) {
            	base64Code = restResultNode.get("base64").textValue();
            	byte[] in_b =FileUtil.base64String2ByteFun(base64Code);
                StorePath path = storageClient.uploadFile(null, new ByteArrayInputStream(in_b), in_b.length, fileEntity.getFileType());
                String storePath = path.getFullPath();
                if (StringUtils.isBlank(storePath)) {
                    logger.error(fileEntity.getFileName() + "上传失败");
                }
                
                //插入数据库
                OcrAdvFuncEntity entityInsertDB=new OcrAdvFuncEntity();
                // 保存处理之后的文件信息
                OcrFileEntity newFileEntity=new OcrFileEntity();

                String id=UUIDUtil.getUUID();
                entityInsertDB.setId(id);
                entityInsertDB.setAdvRecoPath(storePath);
                entityInsertDB.setFileId(fileEntity.getId());
                entityInsertDB.setOpeType(opeType);
                compressPath(in_b, entityInsertDB, fileEntity,newFileEntity);

                newFileEntity.setId(id);
                newFileEntity.setDirId(null);
                newFileEntity.setFileName(fileEntity.getFileName());
                newFileEntity.setFilePage(fileEntity.getFilePage());
                newFileEntity.setFilePath(storePath);
                newFileEntity.setFileType(fileEntity.getFileType());
                newFileEntity.setUserId(fileEntity.getUserId());
                ocrAdvFuncDao.insertSelective(entityInsertDB);
                this.ocrFileDao.insertSelective(newFileEntity);
                //进行图片识别
                this.recogniseService.doRecByFileId(id);

                String advCompPath=entityInsertDB.getAdvCompPath();
                if(StringUtils.isNotEmpty(advCompPath)) {
                	return advCompPath;
                }
                return storePath;
            }
        }
        return null;
    }
    /**
     * 图片压缩
     * <br>
     * 适用场景:	<br>
     * 调用方式:	<br>
     * 业务逻辑说明<br>
     *
     * @param bytes
     * @param entityInsertDB
     * @param fileEntity
     * @return
     * @throws IOException 
     * @autho dgyu
     * @time 2019年10月29日 上午11:07:30
     */
	private void compressPath(byte[] bytes, OcrAdvFuncEntity entityInsertDB,OcrFileEntity fileEntity,OcrFileEntity newFileEntity) throws Exception {
		long fileSize = bytes.length;
		newFileEntity.setFileSize(fileSize);
		if (fileSize > CommonConstant.FORMAT_MIN_SIZE) {
			// 压缩图片文件
			logger.debug("------- 开始图片压缩-------");
			double rate = (double) CommonConstant.FORMAT_MIN_SIZE/ (double) fileSize;
			InputStream is = new ByteArrayInputStream(bytes);
			ByteArrayOutputStream reduceStream = (ByteArrayOutputStream) ImageUtil.reduceImgByRate(is, rate);
			if (reduceStream != null) {
				byte[] reduceBytes = reduceStream.toByteArray();
				long formatSize = reduceBytes.length;
				
				StorePath path = storageClient.uploadFile(null, new ByteArrayInputStream(reduceBytes), formatSize, fileEntity.getFileType());
                String storePath = path.getFullPath();
                if (StringUtils.isBlank(storePath)) {
                    logger.error(fileEntity.getFileName() + "上传失败");
                }
				reduceStream.close();
				// 设置压缩之后图片地址
				entityInsertDB.setAdvCompPath(storePath);

				newFileEntity.setFormatPath(storePath);
				newFileEntity.setFormatSize(formatSize);
				newFileEntity.setFormatType("jpg");
			}
		}
	}
	
	
	
	/*@Override
	public ExportFileInfo exportToFile(List<String> fileIds, String type) {
		Example example = new Example(OcrFileEntity.class);
		Criteria criteria = example.createCriteria();
		criteria.andIn("id", fileIds);
		String ids= fileIds.stream()
	            .collect(Collector.of(
	                    StringBuilder::new,
	                    (sb, s) -> sb.append('\'').append(s).append('\'').append(","),
	                    StringBuilder::append,
	                    sb -> {
	                        sb.setLength(Math.max(0, sb.length() - 1));
	                        return sb.toString();
	                    }));
		example.setOrderByClause("field(id,"+ids+")");
		List<OcrFileEntity> files = ocrFileDao.selectByExample(example);
        if (CollectionUtils.isEmpty(files)) {
            logger.info("文件不存在. fileIds: {}", fileIds);
            return null;
        }
        ExportFileInfo exportFileInfo=this.exportMultiPageFile(files, type);
        return exportFileInfo;
	}*/
	
	
	@Override
	public ExportFileInfo exportToFile(List<String> fileIds, String type) {
		try {
			FileExportStrategy fileExportStrategy = determineExporter(type);
			if (fileExportStrategy == null) {
			    logger.info("不支持该导出类型. exportType: {}", type);
			    return null;
			}
			List<String> list=new ArrayList<String>();
			for(String id:fileIds) {
				Example example = new Example(OcrFileEntity.class);
				Criteria criteria = example.createCriteria();
				criteria.andEqualTo("id", id);
				OcrFileEntity file=this.ocrFileDao.selectOneByExample(example);
				if(null!=file && Constants.SupportExt.PDF.equalsIgnoreCase(file.getFileType())) {
					List<OcrFileEntity> subFiles=this.ocrFileDao.selectSubFile(id);
					List<String> subIds=subFiles.stream().map(OcrFileEntity::getId).collect(Collectors.toList());
					list.addAll(subIds);
				}else {
					list.add(id);
				}
			}
			List<List<com.iflytek.icourt.model.pdf.Page>> pageListParam=new ArrayList<>();
			List<OcrResultEntity> results=this.ocrResultDao.selectAllResults(list);
			for(OcrResultEntity ocrResultEntity:results) {
				StringBuilder ocrResultJsonStr=new StringBuilder();
				StringBuilder result=new StringBuilder();
				result.append(ocrResultEntity.getResult());
				ocrResultJsonStr.append("{\"task_result\":{\"page\":[").append(result).append("]}}");
				String pureJson = JSON.toJSONString(JSON.parseObject(ocrResultJsonStr.toString()));
			    OcrResult ocrResult=JSONObject.parseObject(pureJson, OcrResult.class);
			    List<com.iflytek.icourt.model.pdf.Page> pageList = ocrResult.getTaskResult().getPage();
			    pageListParam.add(pageList);
			}
			byte[] fileBytes=null;
			//word字号
			List<String> exportParam = new ArrayList<>();
			exportParam.add(exportProperties.getWordRtfSize());
			//exportParam.add(exportProperties.getWordFamilyName());
            fileBytes= fileExportStrategy.createFileByOcrResultPageLists(pageListParam, type,exportParam);

			ExportFileInfo exportFileInfo = new ExportFileInfo();
			if(fileIds.size() == 1) {
				Example example = new Example(OcrFileEntity.class);
				Criteria criteria = example.createCriteria();
				criteria.andEqualTo("id", fileIds.get(0));
				OcrFileEntity file=this.ocrFileDao.selectOneByExample(example);
				exportFileInfo.setFileName(file.getFileName() + "." + type);
			}
			exportFileInfo.setContent(fileBytes);
			return exportFileInfo;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

    @Override
    public OutputMessage<Object> pictureRotate(String fileId, String orientation) {
	    if(StringUtils.isAnyEmpty(fileId,orientation)){
	        return new OutputMessage<>(1,"","输入参数为空!");
        }
	    OcrFileEntity ocrFileEntity = new OcrFileEntity();
	    ocrFileEntity.setId(fileId);

        OcrFileEntity queryEntity = ocrFileDao.selectOne(ocrFileEntity);
        if (queryEntity == null || queryEntity.getFilePath() == null){
            return new OutputMessage<>(1,"","查询结果不正确!");
        }
        String filePath = queryEntity.getFormatPath();
        if (filePath == null){
            filePath = queryEntity.getFilePath();
        }
        byte[] bytes = getFileBytes(filePath);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        BufferedImage src = null;
        try {
             src = ImageIO.read(byteArrayInputStream);
        }catch (Exception e){
            e.printStackTrace();
        }
        int angel = Integer.parseInt(orientation);
        angel = 360 - angel;
        BufferedImage des1 = Rotate(src, angel);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try {
            ImageIO.write(des1, "png", byteArrayOutputStream);//写入流中
        }catch (Exception e){
            e.printStackTrace();
        }

        byte[] outputBytes = byteArrayOutputStream.toByteArray();//转换成字节
        BASE64Encoder encoder = new BASE64Encoder();
        String png_base64 = encoder.encodeBuffer(outputBytes).trim();
        png_base64 = png_base64.replaceAll("\n", "").replaceAll("\r", "");
        png_base64 = "data:image/png;base64," + png_base64;
        MyPictureData myPictureData = new MyPictureData(fileId, png_base64);
        return new OutputMessage<>(0, myPictureData,"图片旋转成功!");
    }

    class MyPictureData{
	    private String fileId;
	    private  String png_base64;

	    public MyPictureData(){

        }

        public MyPictureData(String fileId, String png_base64) {
            this.fileId = fileId;
            this.png_base64 = png_base64;
        }

        public String getFileId() {
            return fileId;
        }

        public void setFileId(String fileId) {
            this.fileId = fileId;
        }

        public String getPng_base64() {
            return png_base64;
        }

        public void setPng_base64(String png_base64) {
            this.png_base64 = png_base64;
        }
    }

    /**
     * 对图片进行旋转
     */
    public BufferedImage Rotate(Image src, int angel) {
        int src_width = src.getWidth(null);
        int src_height = src.getHeight(null);
        // 计算旋转后图片的尺寸
        Rectangle rect_des = CalcRotatedSize(new Rectangle(new Dimension(
                src_width, src_height)), angel);
        BufferedImage res = null;
        res = new BufferedImage(rect_des.width, rect_des.height,
                BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = res.createGraphics();
        // 进行转换
        g2.translate((rect_des.width - src_width) / 2,
                (rect_des.height - src_height) / 2);
        g2.rotate(Math.toRadians(angel), src_width / 2, src_height / 2);

        g2.drawImage(src, null, null);
        return res;
    }
    /**
     *计算旋转后的图片
     */
    public Rectangle CalcRotatedSize(Rectangle src, int angel) {
        // 如果旋转的角度大于90度做相应的转换
        if (angel >= 90) {
            if (angel / 90 % 2 == 1) {
                int temp = src.height;
                src.height = src.width;
                src.width = temp;
            }
            angel = angel % 90;
        }

        double r = Math.sqrt(src.height * src.height + src.width * src.width) / 2;
        double len = 2 * Math.sin(Math.toRadians(angel) / 2) * r;
        double angel_alpha = (Math.PI - Math.toRadians(angel)) / 2;
        double angel_dalta_width = Math.atan((double) src.height / src.width);
        double angel_dalta_height = Math.atan((double) src.width / src.height);

        int len_dalta_width = (int) (len * Math.cos(Math.PI - angel_alpha
                - angel_dalta_width));
        int len_dalta_height = (int) (len * Math.cos(Math.PI - angel_alpha
                - angel_dalta_height));
        int des_width = src.width + len_dalta_width * 2;
        int des_height = src.height + len_dalta_height * 2;
        return new Rectangle(new Dimension(des_width, des_height));
    }


    @Override
    public String export(List<String> fileIds, String exportFormat, boolean isCompress, String userId) {
        // fileIds 去重排序, 逗号拼接
    	//去重
    	List<String> distinctList = fileIds.stream().collect(//list是需要去重的list，返回值是去重后的list
                Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(s->s))),//o代表object对象，o.list对象的属性值，根据此属性值去重
                        ArrayList::new));
    	//排序
    	distinctList.sort((a, b) -> a.compareTo(b));

    	fileIds=distinctList;
        String fileIdStr = fileIds.stream().collect(Collectors.joining(","));

        // 查询任务是否已经存在
        // 如果 isCompress 为 true , select * from t_export_task where file_ids = {fileIdStr} and export_format = yy
        // 否则 , select * from t_export_task where file_ids = {fileIdStr} and export_format = {exportFormat}
        
        Example example = new Example(ExportTaskEntity.class);
        Criteria criteria = example.createCriteria();
        criteria.andEqualTo("fileIds", fileIdStr);
        if(isCompress) {
        	criteria.andEqualTo("exportType", exportFormat+".zip");
        }else {
        	criteria.andEqualTo("exportType", exportFormat);
        }
        List<ExportTaskEntity> exportTasks = exportTaskDao.selectByExample(example);
        //ExportTaskEntity record = exportTaskDao.selectOneByExample(example);
        ExportTaskEntity record = null;
        if (CollectionUtils.isNotEmpty(exportTasks)){
            record = exportTasks.get(0);
        }

        String newTaskId = StringUtils.EMPTY;
        String exportType = StringUtils.EMPTY;
        if(null!=record) {// 任务已存在直接返回 id, 方法结束
        	newTaskId=record.getId();
        	if (!isCompress){
                exportType=record.getExportType();
                newTaskId=newTaskId+"&"+record.getFileName()+"."+exportType.substring(exportType.lastIndexOf(".")+1,exportType.length());
                return newTaskId;
            }
        }else {// 任务不存在, 首先插入新的导出任务
        	record=new ExportTaskEntity();
			record.setCreateTime(new Date());
			record.setExporterId(userId);
			if(isCompress) {
			 record.setExportType(exportFormat+".zip");
			}else {
				record.setExportType(exportFormat);
			}
			if(!isCompress && fileIds.size() == 1) {
				//单个文件导出 使用原文件名
				String fileId=fileIds.get(0);
				/*Example e = new Example(OcrFileEntity.class);
				Criteria c = e.createCriteria();
		        c.andEqualTo("id", fileId);*/
		        OcrFileEntity ocrFileEntity = ocrFileDao.selectByPrimaryKey(fileId);
		        if (ocrFileEntity == null){//查询不到文件存在，则返回
		            logger.info("文件不存在");
		            return null;
                }
		        record.setFileName(ocrFileEntity.getFileName());
			}else {
				String fileName=this.queryDistinctDirName(fileIds);
				record.setFileName(fileName);
			}
			record.setFileIds(fileIdStr);
			//record.setFilePath(storePath);
			newTaskId=UUIDUtil.getUUID();
			record.setId(newTaskId);
			record.setStatus(ExportTaskStatusEnum.PROCESSING.toString());
			this.exportTaskDao.insertSelective(record);
        }

        // 否则按是否压缩导出调用不同线程池
        if (isCompress) {
        	compressExportExecutor.execute(new CompressFileExportWorker(newTaskId, fileIds, exportFormat,userId,record));
        } else {
            List<String> list = new ArrayList<>();
            if ("docx".equals(exportFormat) || "wps".equals(exportFormat)){
                //word不进行任务分解
                list.addAll(fileIds);
                normalExportExecutor.submit(new NormalFileExportWorker(newTaskId, fileIds, exportFormat,userId,list));
            }else {
                //非word任务分解
                if (fileIds.size() == 1){//如果是单文件，直接送入异步线程进行执行
                    list.add(fileIds.get(0));
                    normalExportExecutor.submit(new NormalFileExportWorker(newTaskId, fileIds, exportFormat,userId,list));
                }else{
                    //新创建的多文件导出,判断子任务是否存在
                    Example example1 = new Example(ExportTaskEntity.class);
                    example1.createCriteria().andEqualTo("exportType", exportFormat).andIn("fileIds", fileIds);
                    List<ExportTaskEntity> exportTaskEntities = exportTaskDao.selectByExample(example1);
                    List<String> strings = new ArrayList<>();
                    strings.addAll(fileIds);
                    strings.removeAll(exportTaskEntities.stream().map(ExportTaskEntity::getFileIds).collect(Collectors.toList()));//删除已存在的任务
                    if (CollectionUtils.isNotEmpty(strings)){
                        list.addAll(strings);
                        //创建没有的子任务
                        List<ExportTaskEntity> exportTaskEntities1 = new ArrayList<>();
                        Example example2 = new Example(OcrFileEntity.class);
                        example2.createCriteria().andIn("id", strings);
                        List<OcrFileEntity> ocrFileEntities = ocrFileDao.selectByExample(example2);//批量按主键查询，为了得到文件名称
                        for (OcrFileEntity ocrFileEntity : ocrFileEntities){
                            ExportTaskEntity exportTaskEntity1 = new ExportTaskEntity();
                            String subTaskId = UUIDUtil.getUUID();
                            exportTaskEntity1.setId(subTaskId);
                            exportTaskEntity1.setCreateTime(new Date());
                            exportTaskEntity1.setFileIds(ocrFileEntity.getId());
                            exportTaskEntity1.setExportType(exportFormat);
                            exportTaskEntity1.setStatus(ExportTaskStatusEnum.PROCESSING.toString());
                            exportTaskEntity1.setFileName(ocrFileEntity.getFileName());
                            exportTaskEntity1.setExporterId(userId);
                            exportTaskEntities1.add(exportTaskEntity1);
                        }
                        //批量插入任务
                        exportTaskDao.batchInsert(exportTaskEntities1);
                        normalExportExecutor.submit(new NormalFileExportWorker(newTaskId, fileIds, exportFormat,userId,list));
                    }else {
                        normalExportExecutor.submit(new NormalFileExportWorker(newTaskId, fileIds, exportFormat,userId,list));
                    }

                }


            }
        }
        exportType=record.getExportType();
        newTaskId=newTaskId+"&"+record.getFileName()+"."+exportType.substring(exportType.lastIndexOf(".")+1,exportType.length());
        return newTaskId;
    }

    @Override
    public MessageOutput searchTask(List<String> taskIdsList) {
        List<ExportTaskEntity> taskResult = new ArrayList<>();
        Example example = new Example(ExportTaskEntity.class);
        Criteria criteria = example.createCriteria();
        criteria.andIn("id", taskIdsList);
        List<ExportTaskEntity> existTasks = exportTaskDao.selectByExample(example);
        taskIdsList.removeAll(existTasks.stream().map(ExportTaskEntity::getId).collect(Collectors.toList()));//不存在的id的list
        // 构造不存在entity
        List<ExportTaskEntity> invalidTasks = getEntity(taskIdsList, ExportTaskStatusEnum.INVALID.toString());

        List<ExportTaskEntity> finishedTasks = existTasks.stream().filter(t -> t.getStatus().equals(ExportTaskStatusEnum.FINISHED.toString())).collect(Collectors.toList());
        finishedTasks.stream().forEach(e -> e.setFilePath(fdfsUrl + "/" + e.getFilePath()));
        taskResult.addAll(finishedTasks);
        if (CollectionUtils.isNotEmpty(invalidTasks)){
            taskResult.addAll(invalidTasks);
        }
        return new MessageOutput(new State(CommonConstant.FAIL_CODE), taskResult);

    }

    public List<ExportTaskEntity> getEntity(List<String> idList, String status){
        if (CollectionUtils.isEmpty(idList)){
            return null;
        }
        List<ExportTaskEntity> resultList = new ArrayList<>();
        for (String taskId : idList){
            ExportTaskEntity exportTaskEntity = new ExportTaskEntity();
            exportTaskEntity.setStatus(status);
            exportTaskEntity.setId(taskId);
            resultList.add(exportTaskEntity);
        }
        return resultList;
    }

    @Override
    public MessageOutput<?> uploadPdfPassword(String fileId, String password, String ip) {
        FileInfo fileInfo = selectFileInfo(fileId);
        if (fileInfo == null) {
            return new MessageOutput<>(new State(ResultCodeEnum.INVALID_PARAM), "文件不存在");
        }
        else if (!FileStatusEnum.ENCRYPTED.getCode().equals(fileInfo.getStatus())) {
            return new MessageOutput<>(new State(ResultCodeEnum.INVALID_PARAM), "文件状态错误");
        }

        String filePath = fileInfo.getFilePath();
        byte[] fileBytes = new byte[0];
        try {
            String storePath = fdfsUrl + "/" + filePath;
            fileBytes = PoliceDataExchangeUtils.downloadFile(storePath, "", "");
            if (ArrayUtils.isEmpty(fileBytes)) {
                logger.info("文件下载结果为空. fileId: {}, filePath: {}", fileId, storePath);
                return new MessageOutput<>(new State(ResultCodeEnum.FASTDFS_DOWNLOAD_ERROR));
            }
        }
        catch (Exception e) {
            logger.warn("文件下载异常, fileId: {}", fileId, e);
            return new MessageOutput<>(new State(ResultCodeEnum.FASTDFS_DOWNLOAD_ERROR));
        }

        int pageSize = 0;
        try {
            PDDocument document = PDDocument.load(fileBytes, password);
            pageSize = document.getNumberOfPages();
            fileInfo.setFilePage(pageSize);
            fileInfo.setFormatPath(password);
            fileInfo.setStatus(FileStatusEnum.INIT.getCode());

            int remainRecognizePageSize = redisService.getRemainRecognizePageSize(fileInfo.getUserId());
            if (remainRecognizePageSize != -1 && remainRecognizePageSize < pageSize) {
                logger.info("文件页数: {}, 剩余识别页数: {}", pageSize, remainRecognizePageSize);

                deleteFile(fileId);

                return new MessageOutput<>(new State(ResultCodeEnum.RECOGNIZE_PAGE_EXCEED_LIMIT));
            }

            saveFileInfo(fileInfo);
        }
        catch (InvalidPasswordException e) {
            // 删除文件
            deleteFile(fileId);
            
            return new MessageOutput<>(new State(ResultCodeEnum.PDF_REQUIRE_PASSWORD_ERROR));
        }
        catch (IOException e) {
            logger.warn("PDF读取异常, fileId: {}", fileId, e);
            return new MessageOutput<>(new State(ResultCodeEnum.PDF_READ_FAILED));
        }

        FormatTask task = new FormatTask();
        task.setDirId(fileInfo.getDirId());
        task.setIp(ip);
        task.setUserId(fileInfo.getUserId());
        task.setFileInfo(fileInfo);
        task.setFileBytes(fileBytes);
        TaskDispatchSystem.getInstance(null).formatStation.add(task);
        return new MessageOutput<>(new State(ResultCodeEnum.SUCCESS), fileInfo);
    }

    @Override
    public MessageOutput<?> recognizeSubPages(String fileId, List<Integer> pageIndexList) {
        FileInfo parentFile = selectFileInfo(fileId);
        if (parentFile == null) {
            return new MessageOutput<>(new State(ResultCodeEnum.INVALID_PARAM), "文件不存在");
        }

        if (CollectionUtils.isEmpty(pageIndexList)) {
            logger.info("没有传递要识别的子页面下标, 则删除所有已保存的子页面");
            deleteFile(fileId);
        }

        Example queryParam = new Example(OcrFileEntity.class);
        Criteria criteria = queryParam.createCriteria();
        criteria.andEqualTo("parentId", fileId);
        List<OcrFileEntity> ocrFileEntities = ocrFileDao.selectByExample(queryParam);
        if (CollectionUtils.isEmpty(ocrFileEntities)) {
            return new MessageOutput<>(new State(ResultCodeEnum.INVALID_PARAM), "文件没有子页面");
        }

        // 过滤出需要识别的子页面
        List<OcrFileEntity> needRecognizeFiles = ocrFileEntities.stream().filter(f -> pageIndexList.stream().anyMatch(
                i -> i.equals(f.getFilePage()))).sorted(Comparator.comparingInt(OcrFileEntity::getFilePage)).collect(Collectors.toList());

        // 其他不需要识别的子页面删除
        ocrFileEntities.removeAll(needRecognizeFiles);
        if (CollectionUtils.isNotEmpty(ocrFileEntities)) {
            fastdfsDeleteFile(ocrFileEntities);

            Example deleteParam = new Example(OcrFileEntity.class);
            List<String> fileIds = ocrFileEntities.stream().map(OcrFileEntity::getId).collect(Collectors.toList());
            deleteParam.createCriteria().andIn("id", fileIds);
            logger.debug("删除不需要的子页面, subFileIds: {}", JSONUtil.toStrByDefault(fileIds));
            ocrFileDao.deleteByExample(deleteParam);
        }

        // 文件总页数保存到formatSize字段
        parentFile.setFormatSize(Long.valueOf(parentFile.getFilePage()));
        int recogSize = CollectionUtils.size(needRecognizeFiles);
        // 保存实际要识别的页数, 为了后续查询识别进度
        parentFile.setFilePage(recogSize);
        saveFileInfo(parentFile);
        // 更新识别页数统计
        redisService.increamentRecognizePageCount(parentFile.getUserId(), recogSize);

        // 重新设置子页面文件下标
        for (int i = 0; i < needRecognizeFiles.size(); i++) {
            final OcrFileEntity fileEntity = needRecognizeFiles.get(i);
            fileEntity.setFilePage(i + 1);
            ocrFileDao.updateByPrimaryKey(fileEntity);
        }

        for (OcrFileEntity ocrFileEntity : needRecognizeFiles) {
            FileInfo fileInfo = new FileInfo();
            BeanUtils.copyProperties(ocrFileEntity, fileInfo);
            RecognizedTask task = new RecognizedTask();
            task.setFileInfo(fileInfo);
            TaskDispatchSystem.getInstance(worksProperties).recognizedStation.add(task);
        }

        return new MessageOutput<>(new State(ResultCodeEnum.SUCCESS));
    }

    @Override
    public MessageOutput<?> queryRecognizeProgress(String fileId) {
        FileInfo pdfFile = selectFileInfo(fileId);
        if (pdfFile == null) {
            return new MessageOutput<>(new State(ResultCodeEnum.INVALID_PARAM), "文件不存在");
        }
        Integer totalSize = pdfFile.getFilePage();

        Example example = new Example(OcrResultEntity.class);
        example.createCriteria().andEqualTo("groupId", fileId);
        List<OcrResultEntity> resultEntityList = ocrResultDao.selectByExample(example);
        int completeSize = CollectionUtils.size(resultEntityList);

        float process = 0;
        if (totalSize == null || totalSize.equals(0L) || completeSize == 0) {
            process = 0;
        }
        else {
            process = (float) completeSize / totalSize;
        }

        return new MessageOutput<>(new State(ResultCodeEnum.SUCCESS), process * 100);
    }


    @Override
	public MessageOutput<?> exportCheckWithMessage(String fileId, String type) {
        int filePage = 0;
		try {
			OcrFileEntity ocrFileEntity = new OcrFileEntity();
			ocrFileEntity.setId(fileId);
			List<OcrFileEntity> files = ocrFileDao.select(ocrFileEntity);
			if(CollectionUtils.isEmpty(files)) {
				return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE, "文件不存在,fileId="+fileId),false);
			}
			OcrFileEntity OcrFileEntityInDB=files.get(0);
			if(!OcrFileEntityInDB.getIsDelete().equals(FILE_DELETE)) {
				return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE, "文件已被删除,fileId="+fileId),false);
			}
			if (CollectionUtils.isNotEmpty(files) && !OcrFileEntityInDB.getFileType().equalsIgnoreCase(Constants.SupportExt.PDF)) //不是pdf 并且有文件
			{
				//查询识别结果是否为空
			    Example example = new Example(OcrResultEntity.class);
			    example.createCriteria().andEqualTo("fileId",fileId).andIsNotNull("result");
			    OcrResultEntity ocrResultEntity=ocrResultDao.selectOneByExample(example);
			    if(null == ocrResultEntity) {
			    	return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE, "文件"+OcrFileEntityInDB.getFileName()+"."+OcrFileEntityInDB.getFileType()+"识别异常！"),false);
			    }
                if (StringUtils.isNotEmpty(type) && (Constants.SupportExt.XLSX.equalsIgnoreCase(type) ||
                        "et".equalsIgnoreCase(type))) {//不是pdf并且导出类型excel，必须要添加html条件判断
                    if (StringUtils.isEmpty(ocrResultEntity.getHtml())) {
                        return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE,
                                "当前文件" + OcrFileEntityInDB.getFileName() + "." + OcrFileEntityInDB.getFileType() +
                                        "无表格数据！"), false);
                    }
                }
                else {
                    if (StringUtils.isEmpty(ocrResultEntity.getResult())) {
                        return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE,
                                "文件" + OcrFileEntityInDB.getFileName() + "." + OcrFileEntityInDB.getFileType() +
                                        "识别异常！"), false);
                    }
                }
			    filePage = 1;
			    return new MessageOutput<Object>(new State(CommonConstant.SUCCESS_CODE, String.valueOf(filePage)),true);
			}
			//多页pdf导出excel直接返回false，即（不允许这种操作）
			if (StringUtils.isNotEmpty(type) && Constants.SupportExt.XLSX.equalsIgnoreCase(type) && OcrFileEntityInDB.getFilePage() != 1){
				logger.error(OcrFileEntityInDB.getFileName()+"."+OcrFileEntityInDB.getFileType()+"是多页PDF文件，不支持导出excel");
				return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE, "Excel仅支持单页文件导出！"),false);
			}
			//查询识别异常 status=2,3记录
			Example exceptionExample = new Example(OcrResultEntity.class);
			exceptionExample.createCriteria().orEqualTo("status", CommonConstant.RECG_FAIL_ENGINE).orEqualTo("status", CommonConstant.RECG_FAIL_FORMAT);
	        Example.Criteria criteria = exceptionExample.createCriteria();
	        criteria.andEqualTo("groupId", fileId);
	        exceptionExample.and(criteria);
			int exceptionNum = ocrResultDao.selectCountByExample(exceptionExample);//查询识别的页数
			if(exceptionNum >0) {
				return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE, "文件"+OcrFileEntityInDB.getFileName()+"."+OcrFileEntityInDB.getFileType()+"识别异常！"),false);
			}
			//查询识别结果是否为空
			Example example = new Example(OcrResultEntity.class);
			example.createCriteria().andEqualTo("groupId",fileId).andIsNotNull("result");
			int ocrResultEntitiesNum = ocrResultDao.selectCountByExample(example);//查询识别的页数
			if(ocrResultEntitiesNum != files.get(0).getFilePage()) {
				return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE, "文件"+OcrFileEntityInDB.getFileName()+"."+OcrFileEntityInDB.getFileType()+"识别中！"),false);
			}
			filePage = files.get(0).getFilePage();
			if (filePage > 500){
                return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE, "导出文件总页数最大支持500页"),false);
            }
			return new MessageOutput<Object>(new State(CommonConstant.SUCCESS_CODE, String.valueOf(filePage)),true);
		} catch (Exception e) {
			e.printStackTrace();
			return new MessageOutput<Object>(new State(CommonConstant.FAIL_CODE, e.getMessage()),false);
		}
	}
}